bootstrap.go 5.81 KB
Newer Older
1 2 3
package main

import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
4 5 6
	"errors"
	"strings"

7 8 9
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag"
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"

12
	config "github.com/jbenet/go-ipfs/config"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13 14
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
15 16 17 18 19 20 21 22 23 24 25
)

var cmdIpfsBootstrap = &commander.Command{
	UsageLine: "bootstrap",
	Short:     "Show a list of bootstrapped addresses.",
	Long: `ipfs bootstrap - show, or manipulate bootstrap node addresses

Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'.

Commands:

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
26 27 28
	list               Show the boostrap list.
	add <address>      Add a node's address to the bootstrap list.
	remove <address>   Remove an address from the bootstrap list.
29

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30 31
` + bootstrapSecurityWarning,
	Run: bootstrapListCmd,
32 33 34
	Subcommands: []*commander.Command{
		cmdIpfsBootstrapRemove,
		cmdIpfsBootstrapAdd,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
35
		cmdIpfsBootstrapList,
36 37 38 39 40
	},
	Flag: *flag.NewFlagSet("ipfs-bootstrap", flag.ExitOnError),
}

var cmdIpfsBootstrapRemove = &commander.Command{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41 42 43 44 45 46
	UsageLine: "remove <address | peerid>",
	Short:     "Remove addresses from the bootstrap list.",
	Long: `ipfs bootstrap remove - remove addresses from the bootstrap list
` + bootstrapSecurityWarning,
	Run:  bootstrapRemoveCmd,
	Flag: *flag.NewFlagSet("ipfs-bootstrap-remove", flag.ExitOnError),
47 48 49
}

var cmdIpfsBootstrapAdd = &commander.Command{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
50 51 52 53 54 55
	UsageLine: "add <address | peerid>",
	Short:     "Add addresses to the bootstrap list.",
	Long: `ipfs bootstrap add - add addresses to the bootstrap list
` + bootstrapSecurityWarning,
	Run:  bootstrapAddCmd,
	Flag: *flag.NewFlagSet("ipfs-bootstrap-add", flag.ExitOnError),
56 57
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
58 59 60 61 62 63 64 65
var cmdIpfsBootstrapList = &commander.Command{
	UsageLine: "list",
	Short:     "Show addresses in the bootstrap list.",
	Run:       bootstrapListCmd,
	Flag:      *flag.NewFlagSet("ipfs-bootstrap-list", flag.ExitOnError),
}

func bootstrapRemoveCmd(c *commander.Command, inp []string) error {
66 67

	if len(inp) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
68
		return errors.New("remove: no address or peerid specified")
69 70
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
71 72 73 74
	toRemove, err := bootstrapInputToPeers(inp)
	if err != nil {
		return err
	}
75

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76 77 78 79
	cfg, err := getConfig(c)
	if err != nil {
		return err
	}
80

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
81 82
	keep := []*config.BootstrapPeer{}
	remove := []*config.BootstrapPeer{}
83

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
84 85 86
	// function to filer what to keep
	shouldKeep := func(bp *config.BootstrapPeer) bool {
		for _, skipBP := range toRemove {
87

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
88 89
			// IDs must match to skip.
			if bp.PeerID != skipBP.PeerID {
90 91 92
				continue
			}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
93 94 95 96
			// if Addresses match, or skipBP has no addr (wildcard)
			if skipBP.Address == bp.Address || skipBP.Address == "" {
				return false
			}
97
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
98
		return true
99 100
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
101 102 103 104 105 106
	// filter all the existing peers
	for _, currBP := range cfg.Bootstrap {
		if shouldKeep(currBP) {
			keep = append(keep, currBP)
		} else {
			remove = append(remove, currBP)
107
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
108
	}
109

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
110 111 112 113
	// if didn't remove anyone, bail.
	if len(keep) == len(cfg.Bootstrap) {
		return errors.New("remove: peer given did not match any in list")
	}
114

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
115 116 117 118 119
	// write new config
	cfg.Bootstrap = keep
	if err := writeConfig(c, cfg); err != nil {
		return err
	}
120

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
121 122
	for _, bp := range remove {
		u.POut("removed %s\n", bp)
123 124 125 126
	}
	return nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
127
func bootstrapAddCmd(c *commander.Command, inp []string) error {
128 129

	if len(inp) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130
		return errors.New("add: no address specified")
131 132
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
133
	toAdd, err := bootstrapInputToPeers(inp)
134 135 136 137
	if err != nil {
		return err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138 139 140
	cfg, err := getConfig(c)
	if err != nil {
		return err
141 142
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
	// function to check whether a peer is already in the list.
	combine := func(lists ...[]*config.BootstrapPeer) []*config.BootstrapPeer {

		set := map[string]struct{}{}
		final := []*config.BootstrapPeer{}

		for _, list := range lists {
			for _, peer := range list {
				// if already in the set, continue
				_, found := set[peer.String()]
				if found {
					continue
				}

				set[peer.String()] = struct{}{}
				final = append(final, peer)
			}
		}
		return final
162 163
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164 165 166 167 168
	// combine both lists, removing dups.
	cfg.Bootstrap = combine(cfg.Bootstrap, toAdd)
	if err := writeConfig(c, cfg); err != nil {
		return err
	}
169

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
170 171
	for _, bp := range toAdd {
		u.POut("added %s\n", bp)
172 173 174 175
	}
	return nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
176
func bootstrapListCmd(c *commander.Command, inp []string) error {
177

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
178 179 180 181 182 183 184
	cfg, err := getConfig(c)
	if err != nil {
		return err
	}

	for _, bp := range cfg.Bootstrap {
		u.POut("%s\n", bp)
185 186 187
	}

	return nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188
}
189

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
190 191 192 193 194 195 196 197 198 199 200 201 202
func writeConfig(c *commander.Command, cfg *config.Config) error {

	confdir, err := getConfigDir(c)
	if err != nil {
		return err
	}

	filename, err := config.Filename(confdir)
	if err != nil {
		return err
	}

	return config.WriteConfigFile(filename, cfg)
203
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254

func bootstrapInputToPeers(input []string) ([]*config.BootstrapPeer, error) {
	split := func(addr string) (string, string) {
		idx := strings.LastIndex(addr, "/")
		if idx == -1 {
			return "", addr
		}
		return addr[:idx], addr[idx+1:]
	}

	peers := []*config.BootstrapPeer{}
	for _, addr := range input {
		addrS, peeridS := split(addr)

		// make sure addrS parses as a multiaddr.
		if len(addrS) > 0 {
			maddr, err := ma.NewMultiaddr(addrS)
			if err != nil {
				return nil, err
			}

			addrS, err = maddr.String()
			if err != nil {
				return nil, err
			}
		}

		// make sure idS parses as a peer.ID
		peerid, err := mh.FromB58String(peeridS)
		if err != nil {
			return nil, err
		}

		// construct config entry
		peers = append(peers, &config.BootstrapPeer{
			Address: addrS,
			PeerID:  peer.ID(peerid).Pretty(),
		})
	}
	return peers, nil
}

const bootstrapSecurityWarning = `
SECURITY WARNING:

The bootstrap command manipulates the "bootstrap list", which contains
the addresses of bootstrap nodes. These are the *trusted peers* from
which to learn about other peers in the network. Only edit this list
if you understand the risks of adding or removing nodes from this list.

`