main.go 5.34 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!")
Steven Allen's avatar
Steven Allen committed
137
138
139
140
141
142
		if err := doEcho(s); err != nil {
			log.Println(err)
			s.Reset()
		} else {
			s.Close()
		}
Jeromy's avatar
Jeromy committed
143
144
145
	})

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

Hector Sanjuan's avatar
Hector Sanjuan committed
151
152
	// The following code extracts target's the peer ID from the
	// given multiaddress
Jeromy's avatar
Jeromy committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
	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
168
169
170
171
172
	// 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
173

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

Lars Gierth's avatar
Lars Gierth committed
178
	log.Println("opening stream")
Jeromy's avatar
Jeromy committed
179
	// make a new stream from host B to host A
Hector Sanjuan's avatar
Hector Sanjuan committed
180
181
	// 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
182
	s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0")
Jeromy's avatar
Jeromy committed
183
184
185
186
	if err != nil {
		log.Fatalln(err)
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
187
	_, err = s.Write([]byte("Hello, world!\n"))
Jeromy's avatar
Jeromy committed
188
189
190
191
	if err != nil {
		log.Fatalln(err)
	}

Jeromy's avatar
Jeromy committed
192
193
194
195
196
	out, err := ioutil.ReadAll(s)
	if err != nil {
		log.Fatalln(err)
	}

Lars Gierth's avatar
Lars Gierth committed
197
	log.Printf("read reply: %q\n", out)
Jeromy's avatar
Jeromy committed
198
}
Jeromy's avatar
Jeromy committed
199

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

Hector Sanjuan's avatar
Hector Sanjuan committed
208
209
	log.Printf("read: %s\n", str)
	_, err = s.Write([]byte(str))
Steven Allen's avatar
Steven Allen committed
210
	return err
Jeromy's avatar
Jeromy committed
211
}