main.go 5.32 KB
Newer Older
Jeromy's avatar
Jeromy committed
1
2
3
package main

import (
Hector Sanjuan's avatar
Hector Sanjuan committed
4
	"bufio"
5
	"context"
6
	"crypto/rand"
Jeromy's avatar
Jeromy committed
7
8
	"flag"
	"fmt"
9
	"io"
Jeromy's avatar
Jeromy committed
10
11
	"io/ioutil"
	"log"
12
	mrand "math/rand"
Jeromy's avatar
Jeromy committed
13

Hector Sanjuan's avatar
Hector Sanjuan committed
14
	golog "github.com/ipfs/go-log"
Hector Sanjuan's avatar
Hector Sanjuan committed
15
	crypto "github.com/libp2p/go-libp2p-crypto"
Jeromy's avatar
Jeromy committed
16
	host "github.com/libp2p/go-libp2p-host"
Jeromy's avatar
Jeromy committed
17
	net "github.com/libp2p/go-libp2p-net"
Jeromy's avatar
Jeromy committed
18
19
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
Jeromy's avatar
Jeromy committed
20
	swarm "github.com/libp2p/go-libp2p-swarm"
21
	bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
Jeromy's avatar
Jeromy committed
22
	ma "github.com/multiformats/go-multiaddr"
Hector Sanjuan's avatar
Hector Sanjuan committed
23
	gologging "github.com/whyrusleeping/go-logging"
24
25
	msmux "github.com/whyrusleeping/go-smux-multistream"
	yamux "github.com/whyrusleeping/go-smux-yamux"
Jeromy's avatar
Jeromy committed
26
27
)

Hector Sanjuan's avatar
Hector Sanjuan committed
28
29
// makeBasicHost creates a LibP2P host with a random peer ID listening on the
// given multiaddress. It will use secio if secio is true.
30
31
32
33
34
35
36
37
38
39
40
41
func makeBasicHost(listenPort int, secio bool, randseed int64) (host.Host, error) {

	// If the seed is zero, use real cryptographic randomness. Otherwise, use a
	// deterministic randomness source to make generated keys stay the same
	// across multiple runs
	var r io.Reader
	if randseed == 0 {
		r = rand.Reader
	} else {
		r = mrand.New(mrand.NewSource(randseed))
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
42
43
	// Generate a key pair for this host. We will use it at least
	// to obtain a valid host ID.
44
	priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r)
Hector Sanjuan's avatar
Hector Sanjuan committed
45
46
47
48
49
50
	if err != nil {
		return nil, err
	}

	// Obtain Peer ID from public key
	pid, err := peer.IDFromPublicKey(pub)
Jeromy's avatar
Jeromy committed
51
52
53
54
	if err != nil {
		return nil, err
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
55
56
57
58
59
60
61
62
	// Create a multiaddress
	addr, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort))

	if err != nil {
		return nil, err
	}

	// Create a peerstore
Jeromy's avatar
Jeromy committed
63
64
	ps := pstore.NewPeerstore()

Hector Sanjuan's avatar
Hector Sanjuan committed
65
66
	// If using secio, we add the keys to the peerstore
	// for this peer ID.
Jeromy's avatar
Jeromy committed
67
	if secio {
Hector Sanjuan's avatar
Hector Sanjuan committed
68
69
		ps.AddPrivKey(pid, priv)
		ps.AddPubKey(pid, pub)
Jeromy's avatar
Jeromy committed
70
71
	}

72
73
74
75
	// Set up stream multiplexer
	tpt := msmux.NewBlankTransport()
	tpt.AddTransport("/yamux/1.0.0", yamux.DefaultTransport)

Hector Sanjuan's avatar
Hector Sanjuan committed
76
	// Create swarm (implements libP2P Network)
77
	swrm, err := swarm.NewSwarmWithProtector(
Hector Sanjuan's avatar
Hector Sanjuan committed
78
79
80
81
		context.Background(),
		[]ma.Multiaddr{addr},
		pid,
		ps,
82
83
84
85
86
87
88
89
90
		nil,
		tpt,
		nil,
	)
	if err != nil {
		return nil, err
	}

	netw := (*swarm.Network)(swrm)
Jeromy's avatar
Jeromy committed
91

92
	basicHost := bhost.New(netw)
Hector Sanjuan's avatar
Hector Sanjuan committed
93
94
95
96
97
98
99
100
101
102
103
104

	// Build host multiaddress
	hostAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", basicHost.ID().Pretty()))

	// Now we can build a full multiaddress to reach this host
	// by encapsulating both addresses:
	fullAddr := addr.Encapsulate(hostAddr)
	log.Printf("I am %s\n", fullAddr)
	if secio {
		log.Printf("Now run \"./echo -l %d -d %s -secio\" on a different terminal\n", listenPort+1, fullAddr)
	} else {
		log.Printf("Now run \"./echo -l %d -d %s\" on a different terminal\n", listenPort+1, fullAddr)
Jeromy's avatar
Jeromy committed
105
106
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
107
	return basicHost, nil
Jeromy's avatar
Jeromy committed
108
109
110
}

func main() {
Hector Sanjuan's avatar
Hector Sanjuan committed
111
112
113
	// LibP2P code uses golog to log messages. They log with different
	// string IDs (i.e. "swarm"). We can control the verbosity level for
	// all loggers with:
Hector Sanjuan's avatar
Hector Sanjuan committed
114
	golog.SetAllLoggers(gologging.INFO) // Change to DEBUG for extra info
Hector Sanjuan's avatar
Hector Sanjuan committed
115
116

	// Parse options from the command line
Jeromy's avatar
Jeromy committed
117
118
	listenF := flag.Int("l", 0, "wait for incoming connections")
	target := flag.String("d", "", "target peer to dial")
Jeromy's avatar
Jeromy committed
119
	secio := flag.Bool("secio", false, "enable secio")
120
	seed := flag.Int64("seed", 0, "set random seed for id generation")
Jeromy's avatar
Jeromy committed
121
122
	flag.Parse()

Hector Sanjuan's avatar
Hector Sanjuan committed
123
124
125
126
	if *listenF == 0 {
		log.Fatal("Please provide a port to bind on with -l")
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
127
	// Make a host that listens on the given multiaddress
128
	ha, err := makeBasicHost(*listenF, *secio, *seed)
Jeromy's avatar
Jeromy committed
129
130
131
132
	if err != nil {
		log.Fatal(err)
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
133
134
	// Set a stream handler on host A. /echo/1.0.0 is
	// a user-defined protocol name.
Jeromy's avatar
Jeromy committed
135
	ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) {
Hector Sanjuan's avatar
Hector Sanjuan committed
136
		log.Println("Got a new stream!")
Jeromy's avatar
Jeromy committed
137
		defer s.Close()
Hector Sanjuan's avatar
Hector Sanjuan committed
138
		doEcho(s)
Jeromy's avatar
Jeromy committed
139
140
141
	})

	if *target == "" {
Lars Gierth's avatar
Lars Gierth committed
142
		log.Println("listening for connections")
Jeromy's avatar
Jeromy committed
143
		select {} // hang forever
Jeromy's avatar
Jeromy committed
144
	}
Hector Sanjuan's avatar
Hector Sanjuan committed
145
	/**** This is where the listener code ends ****/
Jeromy's avatar
Jeromy committed
146

Hector Sanjuan's avatar
Hector Sanjuan committed
147
148
	// The following code extracts target's the peer ID from the
	// given multiaddress
Jeromy's avatar
Jeromy committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
	ipfsaddr, err := ma.NewMultiaddr(*target)
	if err != nil {
		log.Fatalln(err)
	}

	pid, err := ipfsaddr.ValueForProtocol(ma.P_IPFS)
	if err != nil {
		log.Fatalln(err)
	}

	peerid, err := peer.IDB58Decode(pid)
	if err != nil {
		log.Fatalln(err)
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
164
165
166
167
168
	// Decapsulate the /ipfs/<peerID> part from the target
	// /ip4/<a.b.c.d>/ipfs/<peer> becomes /ip4/<a.b.c.d>
	targetPeerAddr, _ := ma.NewMultiaddr(
		fmt.Sprintf("/ipfs/%s", peer.IDB58Encode(peerid)))
	targetAddr := ipfsaddr.Decapsulate(targetPeerAddr)
Jeromy's avatar
Jeromy committed
169

Hector Sanjuan's avatar
Hector Sanjuan committed
170
171
	// We have a peer ID and a targetAddr so we add it to the peerstore
	// so LibP2P knows how to contact it
172
	ha.Peerstore().AddAddr(peerid, targetAddr, pstore.PermanentAddrTTL)
Jeromy's avatar
Jeromy committed
173

Lars Gierth's avatar
Lars Gierth committed
174
	log.Println("opening stream")
Jeromy's avatar
Jeromy committed
175
	// make a new stream from host B to host A
Hector Sanjuan's avatar
Hector Sanjuan committed
176
177
	// it should be handled on host A by the handler we set above because
	// we use the same /echo/1.0.0 protocol
Lars Gierth's avatar
Lars Gierth committed
178
	s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0")
Jeromy's avatar
Jeromy committed
179
180
181
182
	if err != nil {
		log.Fatalln(err)
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
183
	_, err = s.Write([]byte("Hello, world!\n"))
Jeromy's avatar
Jeromy committed
184
185
186
187
	if err != nil {
		log.Fatalln(err)
	}

Jeromy's avatar
Jeromy committed
188
189
190
191
192
	out, err := ioutil.ReadAll(s)
	if err != nil {
		log.Fatalln(err)
	}

Lars Gierth's avatar
Lars Gierth committed
193
	log.Printf("read reply: %q\n", out)
Jeromy's avatar
Jeromy committed
194
}
Jeromy's avatar
Jeromy committed
195

Hector Sanjuan's avatar
Hector Sanjuan committed
196
// doEcho reads a line of data a stream and writes it back
197
func doEcho(s net.Stream) {
Hector Sanjuan's avatar
Hector Sanjuan committed
198
199
	buf := bufio.NewReader(s)
	str, err := buf.ReadString('\n')
Lars Gierth's avatar
Lars Gierth committed
200
201
202
203
	if err != nil {
		log.Println(err)
		return
	}
Jeromy's avatar
Jeromy committed
204

Hector Sanjuan's avatar
Hector Sanjuan committed
205
206
	log.Printf("read: %s\n", str)
	_, err = s.Write([]byte(str))
Lars Gierth's avatar
Lars Gierth committed
207
208
209
	if err != nil {
		log.Println(err)
		return
Jeromy's avatar
Jeromy committed
210
211
	}
}