main.go 5.13 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"
Jeromy's avatar
Jeromy committed
15
	libp2p "github.com/libp2p/go-libp2p"
Hector Sanjuan's avatar
Hector Sanjuan committed
16
	crypto "github.com/libp2p/go-libp2p-crypto"
Jeromy's avatar
Jeromy committed
17
	host "github.com/libp2p/go-libp2p-host"
Jeromy's avatar
Jeromy committed
18
	net "github.com/libp2p/go-libp2p-net"
Jeromy's avatar
Jeromy committed
19
20
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
Jeromy's avatar
Jeromy committed
21
	swarm "github.com/libp2p/go-libp2p-swarm"
22
	bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
Jeromy's avatar
Jeromy committed
23
	ma "github.com/multiformats/go-multiaddr"
Hector Sanjuan's avatar
Hector Sanjuan committed
24
	gologging "github.com/whyrusleeping/go-logging"
25
26
	msmux "github.com/whyrusleeping/go-smux-multistream"
	yamux "github.com/whyrusleeping/go-smux-yamux"
Jeromy's avatar
Jeromy committed
27
28
)

Hector Sanjuan's avatar
Hector Sanjuan committed
29
30
// makeBasicHost creates a LibP2P host with a random peer ID listening on the
// given multiaddress. It will use secio if secio is true.
31
32
33
34
35
36
37
38
39
40
41
42
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
43
44
	// Generate a key pair for this host. We will use it at least
	// to obtain a valid host ID.
45
	priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r)
Hector Sanjuan's avatar
Hector Sanjuan committed
46
47
48
49
50
51
	if err != nil {
		return nil, err
	}

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

Hector Sanjuan's avatar
Hector Sanjuan committed
56
57
58
59
60
61
62
63
	// 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
64
65
	ps := pstore.NewPeerstore()

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

Jeromy's avatar
Jeromy committed
73
74
75
	basicHost, err := libp2p.NewWithCfg(ctx, &libp2p.Config{
		ListenAddrs: []ma.Multiaddr{addr},
	})
76
77
78
79
	if err != nil {
		return nil, err
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
80
81
82
83
84
85
86
87
88
89
90
	// 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
91
92
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
93
	return basicHost, nil
Jeromy's avatar
Jeromy committed
94
95
96
}

func main() {
Hector Sanjuan's avatar
Hector Sanjuan committed
97
98
99
	// 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
100
	golog.SetAllLoggers(gologging.INFO) // Change to DEBUG for extra info
Hector Sanjuan's avatar
Hector Sanjuan committed
101
102

	// Parse options from the command line
Jeromy's avatar
Jeromy committed
103
104
	listenF := flag.Int("l", 0, "wait for incoming connections")
	target := flag.String("d", "", "target peer to dial")
Jeromy's avatar
Jeromy committed
105
	secio := flag.Bool("secio", false, "enable secio")
106
	seed := flag.Int64("seed", 0, "set random seed for id generation")
Jeromy's avatar
Jeromy committed
107
108
	flag.Parse()

Hector Sanjuan's avatar
Hector Sanjuan committed
109
110
111
112
	if *listenF == 0 {
		log.Fatal("Please provide a port to bind on with -l")
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
113
	// Make a host that listens on the given multiaddress
114
	ha, err := makeBasicHost(*listenF, *secio, *seed)
Jeromy's avatar
Jeromy committed
115
116
117
118
	if err != nil {
		log.Fatal(err)
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
119
120
	// Set a stream handler on host A. /echo/1.0.0 is
	// a user-defined protocol name.
Jeromy's avatar
Jeromy committed
121
	ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) {
Hector Sanjuan's avatar
Hector Sanjuan committed
122
		log.Println("Got a new stream!")
Steven Allen's avatar
Steven Allen committed
123
124
125
126
127
128
		if err := doEcho(s); err != nil {
			log.Println(err)
			s.Reset()
		} else {
			s.Close()
		}
Jeromy's avatar
Jeromy committed
129
130
131
	})

	if *target == "" {
Lars Gierth's avatar
Lars Gierth committed
132
		log.Println("listening for connections")
Jeromy's avatar
Jeromy committed
133
		select {} // hang forever
Jeromy's avatar
Jeromy committed
134
	}
Hector Sanjuan's avatar
Hector Sanjuan committed
135
	/**** This is where the listener code ends ****/
Jeromy's avatar
Jeromy committed
136

Hector Sanjuan's avatar
Hector Sanjuan committed
137
138
	// The following code extracts target's the peer ID from the
	// given multiaddress
Jeromy's avatar
Jeromy committed
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
	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
154
155
156
157
158
	// 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
159

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

Lars Gierth's avatar
Lars Gierth committed
164
	log.Println("opening stream")
Jeromy's avatar
Jeromy committed
165
	// make a new stream from host B to host A
Hector Sanjuan's avatar
Hector Sanjuan committed
166
167
	// 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
168
	s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0")
Jeromy's avatar
Jeromy committed
169
170
171
172
	if err != nil {
		log.Fatalln(err)
	}

Hector Sanjuan's avatar
Hector Sanjuan committed
173
	_, err = s.Write([]byte("Hello, world!\n"))
Jeromy's avatar
Jeromy committed
174
175
176
177
	if err != nil {
		log.Fatalln(err)
	}

Jeromy's avatar
Jeromy committed
178
179
180
181
182
	out, err := ioutil.ReadAll(s)
	if err != nil {
		log.Fatalln(err)
	}

Lars Gierth's avatar
Lars Gierth committed
183
	log.Printf("read reply: %q\n", out)
Jeromy's avatar
Jeromy committed
184
}
Jeromy's avatar
Jeromy committed
185

Hector Sanjuan's avatar
Hector Sanjuan committed
186
// doEcho reads a line of data a stream and writes it back
Steven Allen's avatar
Steven Allen committed
187
func doEcho(s net.Stream) error {
Hector Sanjuan's avatar
Hector Sanjuan committed
188
189
	buf := bufio.NewReader(s)
	str, err := buf.ReadString('\n')
Lars Gierth's avatar
Lars Gierth committed
190
	if err != nil {
Steven Allen's avatar
Steven Allen committed
191
		return err
Lars Gierth's avatar
Lars Gierth committed
192
	}
Jeromy's avatar
Jeromy committed
193

Hector Sanjuan's avatar
Hector Sanjuan committed
194
195
	log.Printf("read: %s\n", str)
	_, err = s.Write([]byte(str))
Steven Allen's avatar
Steven Allen committed
196
	return err
Jeromy's avatar
Jeromy committed
197
}