github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/swarm.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"path"
     9  	"sort"
    10  
    11  	cmds "github.com/ipfs/go-ipfs/commands"
    12  	swarm "github.com/ipfs/go-ipfs/p2p/net/swarm"
    13  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    14  	iaddr "github.com/ipfs/go-ipfs/util/ipfsaddr"
    15  
    16  	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    17  	mafilter "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
    18  )
    19  
    20  type stringList struct {
    21  	Strings []string
    22  }
    23  
    24  type addrMap struct {
    25  	Addrs map[string][]string
    26  }
    27  
    28  var SwarmCmd = &cmds.Command{
    29  	Helptext: cmds.HelpText{
    30  		Tagline: "swarm inspection tool",
    31  		Synopsis: `
    32  ipfs swarm peers                - List peers with open connections
    33  ipfs swarm addrs                - List known addresses. Useful to debug.
    34  ipfs swarm connect <address>    - Open connection to a given address
    35  ipfs swarm disconnect <address> - Close connection to a given address
    36  ipfs swarm filters				- Manipulate filters addresses
    37  `,
    38  		ShortDescription: `
    39  ipfs swarm is a tool to manipulate the network swarm. The swarm is the
    40  component that opens, listens for, and maintains connections to other
    41  ipfs peers in the internet.
    42  `,
    43  	},
    44  	Subcommands: map[string]*cmds.Command{
    45  		"peers":      swarmPeersCmd,
    46  		"addrs":      swarmAddrsCmd,
    47  		"connect":    swarmConnectCmd,
    48  		"disconnect": swarmDisconnectCmd,
    49  		"filters":    swarmFiltersCmd,
    50  	},
    51  }
    52  
    53  var swarmPeersCmd = &cmds.Command{
    54  	Helptext: cmds.HelpText{
    55  		Tagline: "List peers with open connections",
    56  		ShortDescription: `
    57  ipfs swarm peers lists the set of peers this node is connected to.
    58  `,
    59  	},
    60  	Run: func(req cmds.Request, res cmds.Response) {
    61  
    62  		log.Debug("ipfs swarm peers")
    63  		n, err := req.InvocContext().GetNode()
    64  		if err != nil {
    65  			res.SetError(err, cmds.ErrNormal)
    66  			return
    67  		}
    68  
    69  		if n.PeerHost == nil {
    70  			res.SetError(errNotOnline, cmds.ErrClient)
    71  			return
    72  		}
    73  
    74  		conns := n.PeerHost.Network().Conns()
    75  		addrs := make([]string, len(conns))
    76  		for i, c := range conns {
    77  			pid := c.RemotePeer()
    78  			addr := c.RemoteMultiaddr()
    79  			addrs[i] = fmt.Sprintf("%s/ipfs/%s", addr, pid.Pretty())
    80  		}
    81  
    82  		sort.Sort(sort.StringSlice(addrs))
    83  		res.SetOutput(&stringList{addrs})
    84  	},
    85  	Marshalers: cmds.MarshalerMap{
    86  		cmds.Text: stringListMarshaler,
    87  	},
    88  	Type: stringList{},
    89  }
    90  
    91  var swarmAddrsCmd = &cmds.Command{
    92  	Helptext: cmds.HelpText{
    93  		Tagline: "List known addresses. Useful to debug.",
    94  		ShortDescription: `
    95  ipfs swarm addrs lists all addresses this node is aware of.
    96  `,
    97  	},
    98  	Subcommands: map[string]*cmds.Command{
    99  		"local": swarmAddrsLocalCmd,
   100  	},
   101  	Run: func(req cmds.Request, res cmds.Response) {
   102  
   103  		n, err := req.InvocContext().GetNode()
   104  		if err != nil {
   105  			res.SetError(err, cmds.ErrNormal)
   106  			return
   107  		}
   108  
   109  		if n.PeerHost == nil {
   110  			res.SetError(errNotOnline, cmds.ErrClient)
   111  			return
   112  		}
   113  
   114  		addrs := make(map[string][]string)
   115  		ps := n.PeerHost.Network().Peerstore()
   116  		for _, p := range ps.Peers() {
   117  			s := p.Pretty()
   118  			for _, a := range ps.Addrs(p) {
   119  				addrs[s] = append(addrs[s], a.String())
   120  			}
   121  			sort.Sort(sort.StringSlice(addrs[s]))
   122  		}
   123  
   124  		res.SetOutput(&addrMap{Addrs: addrs})
   125  	},
   126  	Marshalers: cmds.MarshalerMap{
   127  		cmds.Text: func(res cmds.Response) (io.Reader, error) {
   128  			m, ok := res.Output().(*addrMap)
   129  			if !ok {
   130  				return nil, errors.New("failed to cast map[string]string")
   131  			}
   132  
   133  			// sort the ids first
   134  			ids := make([]string, 0, len(m.Addrs))
   135  			for p := range m.Addrs {
   136  				ids = append(ids, p)
   137  			}
   138  			sort.Sort(sort.StringSlice(ids))
   139  
   140  			buf := new(bytes.Buffer)
   141  			for _, p := range ids {
   142  				paddrs := m.Addrs[p]
   143  				buf.WriteString(fmt.Sprintf("%s (%d)\n", p, len(paddrs)))
   144  				for _, addr := range paddrs {
   145  					buf.WriteString("\t" + addr + "\n")
   146  				}
   147  			}
   148  			return buf, nil
   149  		},
   150  	},
   151  	Type: addrMap{},
   152  }
   153  
   154  var swarmAddrsLocalCmd = &cmds.Command{
   155  	Helptext: cmds.HelpText{
   156  		Tagline: "List local addresses.",
   157  		ShortDescription: `
   158  ipfs swarm addrs local lists all local addresses the node is listening on.
   159  `,
   160  	},
   161  	Options: []cmds.Option{
   162  		cmds.BoolOption("id", "Show peer ID in addresses"),
   163  	},
   164  	Run: func(req cmds.Request, res cmds.Response) {
   165  
   166  		n, err := req.InvocContext().GetNode()
   167  		if err != nil {
   168  			res.SetError(err, cmds.ErrNormal)
   169  			return
   170  		}
   171  
   172  		if n.PeerHost == nil {
   173  			res.SetError(errNotOnline, cmds.ErrClient)
   174  			return
   175  		}
   176  
   177  		showid, _, _ := req.Option("id").Bool()
   178  		id := n.Identity.Pretty()
   179  
   180  		var addrs []string
   181  		for _, addr := range n.PeerHost.Addrs() {
   182  			saddr := addr.String()
   183  			if showid {
   184  				saddr = path.Join(saddr, "ipfs", id)
   185  			}
   186  			addrs = append(addrs, saddr)
   187  		}
   188  		sort.Sort(sort.StringSlice(addrs))
   189  
   190  		res.SetOutput(&stringList{addrs})
   191  	},
   192  	Type: stringList{},
   193  	Marshalers: cmds.MarshalerMap{
   194  		cmds.Text: stringListMarshaler,
   195  	},
   196  }
   197  
   198  var swarmConnectCmd = &cmds.Command{
   199  	Helptext: cmds.HelpText{
   200  		Tagline: "Open connection to a given address",
   201  		ShortDescription: `
   202  'ipfs swarm connect' opens a new direct connection to a peer address.
   203  
   204  The address format is an ipfs multiaddr:
   205  
   206  ipfs swarm connect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
   207  `,
   208  	},
   209  	Arguments: []cmds.Argument{
   210  		cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(),
   211  	},
   212  	Run: func(req cmds.Request, res cmds.Response) {
   213  		ctx := req.Context()
   214  
   215  		n, err := req.InvocContext().GetNode()
   216  		if err != nil {
   217  			res.SetError(err, cmds.ErrNormal)
   218  			return
   219  		}
   220  
   221  		addrs := req.Arguments()
   222  
   223  		if n.PeerHost == nil {
   224  			res.SetError(errNotOnline, cmds.ErrClient)
   225  			return
   226  		}
   227  
   228  		pis, err := peersWithAddresses(addrs)
   229  		if err != nil {
   230  			res.SetError(err, cmds.ErrNormal)
   231  			return
   232  		}
   233  
   234  		output := make([]string, len(pis))
   235  		for i, pi := range pis {
   236  			output[i] = "connect " + pi.ID.Pretty()
   237  
   238  			err := n.PeerHost.Connect(ctx, pi)
   239  			if err != nil {
   240  				output[i] += " failure: " + err.Error()
   241  			} else {
   242  				output[i] += " success"
   243  			}
   244  		}
   245  
   246  		res.SetOutput(&stringList{output})
   247  	},
   248  	Marshalers: cmds.MarshalerMap{
   249  		cmds.Text: stringListMarshaler,
   250  	},
   251  	Type: stringList{},
   252  }
   253  
   254  var swarmDisconnectCmd = &cmds.Command{
   255  	Helptext: cmds.HelpText{
   256  		Tagline: "Close connection to a given address",
   257  		ShortDescription: `
   258  'ipfs swarm disconnect' closes a connection to a peer address. The address format
   259  is an ipfs multiaddr:
   260  
   261  ipfs swarm disconnect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
   262  `,
   263  	},
   264  	Arguments: []cmds.Argument{
   265  		cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(),
   266  	},
   267  	Run: func(req cmds.Request, res cmds.Response) {
   268  		n, err := req.InvocContext().GetNode()
   269  		if err != nil {
   270  			res.SetError(err, cmds.ErrNormal)
   271  			return
   272  		}
   273  
   274  		addrs := req.Arguments()
   275  
   276  		if n.PeerHost == nil {
   277  			res.SetError(errNotOnline, cmds.ErrClient)
   278  			return
   279  		}
   280  
   281  		iaddrs, err := parseAddresses(addrs)
   282  		if err != nil {
   283  			res.SetError(err, cmds.ErrNormal)
   284  			return
   285  		}
   286  
   287  		output := make([]string, len(iaddrs))
   288  		for i, addr := range iaddrs {
   289  			taddr := addr.Transport()
   290  			output[i] = "disconnect " + addr.ID().Pretty()
   291  
   292  			found := false
   293  			conns := n.PeerHost.Network().ConnsToPeer(addr.ID())
   294  			for _, conn := range conns {
   295  				if !conn.RemoteMultiaddr().Equal(taddr) {
   296  					log.Debug("it's not", conn.RemoteMultiaddr(), taddr)
   297  					continue
   298  				}
   299  
   300  				if err := conn.Close(); err != nil {
   301  					output[i] += " failure: " + err.Error()
   302  				} else {
   303  					output[i] += " success"
   304  				}
   305  				found = true
   306  				break
   307  			}
   308  
   309  			if !found {
   310  				output[i] += " failure: conn not found"
   311  			}
   312  		}
   313  		res.SetOutput(&stringList{output})
   314  	},
   315  	Marshalers: cmds.MarshalerMap{
   316  		cmds.Text: stringListMarshaler,
   317  	},
   318  	Type: stringList{},
   319  }
   320  
   321  func stringListMarshaler(res cmds.Response) (io.Reader, error) {
   322  	list, ok := res.Output().(*stringList)
   323  	if !ok {
   324  		return nil, errors.New("failed to cast []string")
   325  	}
   326  
   327  	buf := new(bytes.Buffer)
   328  	for _, s := range list.Strings {
   329  		buf.WriteString(s)
   330  		buf.WriteString("\n")
   331  	}
   332  	return buf, nil
   333  }
   334  
   335  // parseAddresses is a function that takes in a slice of string peer addresses
   336  // (multiaddr + peerid) and returns slices of multiaddrs and peerids.
   337  func parseAddresses(addrs []string) (iaddrs []iaddr.IPFSAddr, err error) {
   338  	iaddrs = make([]iaddr.IPFSAddr, len(addrs))
   339  	for i, saddr := range addrs {
   340  		iaddrs[i], err = iaddr.ParseString(saddr)
   341  		if err != nil {
   342  			return nil, cmds.ClientError("invalid peer address: " + err.Error())
   343  		}
   344  	}
   345  	return
   346  }
   347  
   348  // peersWithAddresses is a function that takes in a slice of string peer addresses
   349  // (multiaddr + peerid) and returns a slice of properly constructed peers
   350  func peersWithAddresses(addrs []string) (pis []peer.PeerInfo, err error) {
   351  	iaddrs, err := parseAddresses(addrs)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	for _, iaddr := range iaddrs {
   357  		pis = append(pis, peer.PeerInfo{
   358  			ID:    iaddr.ID(),
   359  			Addrs: []ma.Multiaddr{iaddr.Transport()},
   360  		})
   361  	}
   362  	return pis, nil
   363  }
   364  
   365  var swarmFiltersCmd = &cmds.Command{
   366  	Helptext: cmds.HelpText{
   367  		Tagline: "Manipulate address filters",
   368  		ShortDescription: `
   369  'ipfs swarm filters' will list out currently applied filters. Its subcommands can be used
   370  to add or remove said filters. Filters are specified using the multiaddr-filter format:
   371  
   372  example:
   373  
   374      /ip4/192.168.0.0/ipcidr/16
   375  
   376  Where the above is equivalent to the standard CIDR:
   377  
   378      192.168.0.0/16
   379  
   380  Filters default to those specified under the "Swarm.AddrFilters" config key.
   381  `,
   382  	},
   383  	Subcommands: map[string]*cmds.Command{
   384  		"add": swarmFiltersAddCmd,
   385  		"rm":  swarmFiltersRmCmd,
   386  	},
   387  	Run: func(req cmds.Request, res cmds.Response) {
   388  		n, err := req.InvocContext().GetNode()
   389  		if err != nil {
   390  			res.SetError(err, cmds.ErrNormal)
   391  			return
   392  		}
   393  
   394  		if n.PeerHost == nil {
   395  			res.SetError(errNotOnline, cmds.ErrNormal)
   396  			return
   397  		}
   398  
   399  		snet, ok := n.PeerHost.Network().(*swarm.Network)
   400  		if !ok {
   401  			res.SetError(errors.New("failed to cast network to swarm network"), cmds.ErrNormal)
   402  			return
   403  		}
   404  
   405  		var output []string
   406  		for _, f := range snet.Filters.Filters() {
   407  			s, err := mafilter.ConvertIPNet(f)
   408  			if err != nil {
   409  				res.SetError(err, cmds.ErrNormal)
   410  				return
   411  			}
   412  			output = append(output, s)
   413  		}
   414  		res.SetOutput(&stringList{output})
   415  	},
   416  	Marshalers: cmds.MarshalerMap{
   417  		cmds.Text: stringListMarshaler,
   418  	},
   419  	Type: stringList{},
   420  }
   421  
   422  var swarmFiltersAddCmd = &cmds.Command{
   423  	Helptext: cmds.HelpText{
   424  		Tagline: "add an address filter",
   425  		ShortDescription: `
   426  'ipfs swarm filters add' will add an address filter to the daemons swarm.
   427  Filters applied this way will not persist daemon reboots, to acheive that,
   428  add your filters to the ipfs config file.
   429  `,
   430  	},
   431  	Arguments: []cmds.Argument{
   432  		cmds.StringArg("address", true, true, "multiaddr to filter").EnableStdin(),
   433  	},
   434  	Run: func(req cmds.Request, res cmds.Response) {
   435  		n, err := req.InvocContext().GetNode()
   436  		if err != nil {
   437  			res.SetError(err, cmds.ErrNormal)
   438  			return
   439  		}
   440  
   441  		if n.PeerHost == nil {
   442  			res.SetError(errNotOnline, cmds.ErrNormal)
   443  			return
   444  		}
   445  
   446  		snet, ok := n.PeerHost.Network().(*swarm.Network)
   447  		if !ok {
   448  			res.SetError(errors.New("failed to cast network to swarm network"), cmds.ErrNormal)
   449  			return
   450  		}
   451  
   452  		for _, arg := range req.Arguments() {
   453  			mask, err := mafilter.NewMask(arg)
   454  			if err != nil {
   455  				res.SetError(err, cmds.ErrNormal)
   456  				return
   457  			}
   458  
   459  			snet.Filters.AddDialFilter(mask)
   460  		}
   461  	},
   462  }
   463  
   464  var swarmFiltersRmCmd = &cmds.Command{
   465  	Helptext: cmds.HelpText{
   466  		Tagline: "remove an address filter",
   467  		ShortDescription: `
   468  'ipfs swarm filters rm' will remove an address filter from the daemons swarm.
   469  Filters removed this way will not persist daemon reboots, to acheive that,
   470  remove your filters from the ipfs config file.
   471  `,
   472  	},
   473  	Arguments: []cmds.Argument{
   474  		cmds.StringArg("address", true, true, "multiaddr filter to remove").EnableStdin(),
   475  	},
   476  	Run: func(req cmds.Request, res cmds.Response) {
   477  		n, err := req.InvocContext().GetNode()
   478  		if err != nil {
   479  			res.SetError(err, cmds.ErrNormal)
   480  			return
   481  		}
   482  
   483  		if n.PeerHost == nil {
   484  			res.SetError(errNotOnline, cmds.ErrNormal)
   485  			return
   486  		}
   487  
   488  		snet, ok := n.PeerHost.Network().(*swarm.Network)
   489  		if !ok {
   490  			res.SetError(errors.New("failed to cast network to swarm network"), cmds.ErrNormal)
   491  			return
   492  		}
   493  
   494  		if req.Arguments()[0] == "all" || req.Arguments()[0] == "*" {
   495  			fs := snet.Filters.Filters()
   496  			for _, f := range fs {
   497  				snet.Filters.Remove(f)
   498  			}
   499  			return
   500  		}
   501  
   502  		for _, arg := range req.Arguments() {
   503  			mask, err := mafilter.NewMask(arg)
   504  			if err != nil {
   505  				res.SetError(err, cmds.ErrNormal)
   506  				return
   507  			}
   508  
   509  			snet.Filters.Remove(mask)
   510  		}
   511  	},
   512  }