├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── addrbook.go ├── addrbook_test.go ├── config.go ├── connection.go ├── connection_test.go ├── fuzz.go ├── glide.lock ├── glide.yaml ├── ip_range_counter.go ├── listener.go ├── listener_test.go ├── log.go ├── netaddress.go ├── netaddress_test.go ├── peer.go ├── peer_set.go ├── peer_set_test.go ├── peer_test.go ├── pex_reactor.go ├── pex_reactor_test.go ├── secret_connection.go ├── secret_connection_test.go ├── switch.go ├── switch_test.go ├── types.go ├── upnp ├── README.md ├── log.go ├── probe.go └── upnp.go ├── util.go └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .glide 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.5.0 (April 21, 2017) 4 | 5 | BREAKING CHANGES: 6 | 7 | - Remove or unexport methods from FuzzedConnection: Active, Mode, ProbDropRW, ProbDropConn, ProbSleep, MaxDelayMilliseconds, Fuzz 8 | - switch.AddPeerWithConnection is unexported and replaced by switch.AddPeer 9 | - switch.DialPeerWithAddress takes a bool, setting the peer as persistent or not 10 | 11 | FEATURES: 12 | 13 | - Persistent peers: any peer considered a "seed" will be reconnected to when the connection is dropped 14 | 15 | 16 | IMPROVEMENTS: 17 | 18 | - Many more tests and comments 19 | - Refactor configurations for less dependence on go-config. Introduces new structs PeerConfig, MConnConfig, FuzzConnConfig 20 | - New methods on peer: CloseConn, HandshakeTimeout, IsPersistent, Addr, PubKey 21 | - NewNetAddress supports a testing mode where the address defaults to 0.0.0.0:0 22 | 23 | 24 | ## 0.4.0 (March 6, 2017) 25 | 26 | BREAKING CHANGES: 27 | 28 | - DialSeeds now takes an AddrBook and returns an error: `DialSeeds(*AddrBook, []string) error` 29 | - NewNetAddressString now returns an error: `NewNetAddressString(string) (*NetAddress, error)` 30 | 31 | FEATURES: 32 | 33 | - `NewNetAddressStrings([]string) ([]*NetAddress, error)` 34 | - `AddrBook.Save()` 35 | 36 | IMPROVEMENTS: 37 | 38 | - PexReactor responsible for starting and stopping the AddrBook 39 | 40 | BUG FIXES: 41 | 42 | - DialSeeds returns an error instead of panicking on bad addresses 43 | 44 | ## 0.3.5 (January 12, 2017) 45 | 46 | FEATURES 47 | 48 | - Toggle strict routability in the AddrBook 49 | 50 | BUG FIXES 51 | 52 | - Close filtered out connections 53 | - Fixes for MakeConnectedSwitches and Connect2Switches 54 | 55 | ## 0.3.4 (August 10, 2016) 56 | 57 | FEATURES: 58 | 59 | - Optionally filter connections by address or public key 60 | 61 | ## 0.3.3 (May 12, 2016) 62 | 63 | FEATURES: 64 | 65 | - FuzzConn 66 | 67 | ## 0.3.2 (March 12, 2016) 68 | 69 | IMPROVEMENTS: 70 | 71 | - Memory optimizations 72 | 73 | ## 0.3.1 () 74 | 75 | FEATURES: 76 | 77 | - Configurable parameters 78 | 79 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:latest 2 | 3 | RUN curl https://glide.sh/get | sh 4 | 5 | RUN mkdir -p /go/src/github.com/tendermint/go-p2p 6 | WORKDIR /go/src/github.com/tendermint/go-p2p 7 | 8 | COPY glide.yaml /go/src/github.com/tendermint/go-p2p/ 9 | COPY glide.lock /go/src/github.com/tendermint/go-p2p/ 10 | 11 | RUN glide install 12 | 13 | COPY . /go/src/github.com/tendermint/go-p2p 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Tendermint Go-P2P 2 | Copyright (C) 2015 Tendermint 3 | 4 | 5 | 6 | Apache License 7 | Version 2.0, January 2004 8 | https://www.apache.org/licenses/ 9 | 10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 11 | 12 | 1. Definitions. 13 | 14 | "License" shall mean the terms and conditions for use, reproduction, 15 | and distribution as defined by Sections 1 through 9 of this document. 16 | 17 | "Licensor" shall mean the copyright owner or entity authorized by 18 | the copyright owner that is granting the License. 19 | 20 | "Legal Entity" shall mean the union of the acting entity and all 21 | other entities that control, are controlled by, or are under common 22 | control with that entity. For the purposes of this definition, 23 | "control" means (i) the power, direct or indirect, to cause the 24 | direction or management of such entity, whether by contract or 25 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 26 | outstanding shares, or (iii) beneficial ownership of such entity. 27 | 28 | "You" (or "Your") shall mean an individual or Legal Entity 29 | exercising permissions granted by this License. 30 | 31 | "Source" form shall mean the preferred form for making modifications, 32 | including but not limited to software source code, documentation 33 | source, and configuration files. 34 | 35 | "Object" form shall mean any form resulting from mechanical 36 | transformation or translation of a Source form, including but 37 | not limited to compiled object code, generated documentation, 38 | and conversions to other media types. 39 | 40 | "Work" shall mean the work of authorship, whether in Source or 41 | Object form, made available under the License, as indicated by a 42 | copyright notice that is included in or attached to the work 43 | (an example is provided in the Appendix below). 44 | 45 | "Derivative Works" shall mean any work, whether in Source or Object 46 | form, that is based on (or derived from) the Work and for which the 47 | editorial revisions, annotations, elaborations, or other modifications 48 | represent, as a whole, an original work of authorship. For the purposes 49 | of this License, Derivative Works shall not include works that remain 50 | separable from, or merely link (or bind by name) to the interfaces of, 51 | the Work and Derivative Works thereof. 52 | 53 | "Contribution" shall mean any work of authorship, including 54 | the original version of the Work and any modifications or additions 55 | to that Work or Derivative Works thereof, that is intentionally 56 | submitted to Licensor for inclusion in the Work by the copyright owner 57 | or by an individual or Legal Entity authorized to submit on behalf of 58 | the copyright owner. For the purposes of this definition, "submitted" 59 | means any form of electronic, verbal, or written communication sent 60 | to the Licensor or its representatives, including but not limited to 61 | communication on electronic mailing lists, source code control systems, 62 | and issue tracking systems that are managed by, or on behalf of, the 63 | Licensor for the purpose of discussing and improving the Work, but 64 | excluding communication that is conspicuously marked or otherwise 65 | designated in writing by the copyright owner as "Not a Contribution." 66 | 67 | "Contributor" shall mean Licensor and any individual or Legal Entity 68 | on behalf of whom a Contribution has been received by Licensor and 69 | subsequently incorporated within the Work. 70 | 71 | 2. Grant of Copyright License. Subject to the terms and conditions of 72 | this License, each Contributor hereby grants to You a perpetual, 73 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 74 | copyright license to reproduce, prepare Derivative Works of, 75 | publicly display, publicly perform, sublicense, and distribute the 76 | Work and such Derivative Works in Source or Object form. 77 | 78 | 3. Grant of Patent License. Subject to the terms and conditions of 79 | this License, each Contributor hereby grants to You a perpetual, 80 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 81 | (except as stated in this section) patent license to make, have made, 82 | use, offer to sell, sell, import, and otherwise transfer the Work, 83 | where such license applies only to those patent claims licensable 84 | by such Contributor that are necessarily infringed by their 85 | Contribution(s) alone or by combination of their Contribution(s) 86 | with the Work to which such Contribution(s) was submitted. If You 87 | institute patent litigation against any entity (including a 88 | cross-claim or counterclaim in a lawsuit) alleging that the Work 89 | or a Contribution incorporated within the Work constitutes direct 90 | or contributory patent infringement, then any patent licenses 91 | granted to You under this License for that Work shall terminate 92 | as of the date such litigation is filed. 93 | 94 | 4. Redistribution. You may reproduce and distribute copies of the 95 | Work or Derivative Works thereof in any medium, with or without 96 | modifications, and in Source or Object form, provided that You 97 | meet the following conditions: 98 | 99 | (a) You must give any other recipients of the Work or 100 | Derivative Works a copy of this License; and 101 | 102 | (b) You must cause any modified files to carry prominent notices 103 | stating that You changed the files; and 104 | 105 | (c) You must retain, in the Source form of any Derivative Works 106 | that You distribute, all copyright, patent, trademark, and 107 | attribution notices from the Source form of the Work, 108 | excluding those notices that do not pertain to any part of 109 | the Derivative Works; and 110 | 111 | (d) If the Work includes a "NOTICE" text file as part of its 112 | distribution, then any Derivative Works that You distribute must 113 | include a readable copy of the attribution notices contained 114 | within such NOTICE file, excluding those notices that do not 115 | pertain to any part of the Derivative Works, in at least one 116 | of the following places: within a NOTICE text file distributed 117 | as part of the Derivative Works; within the Source form or 118 | documentation, if provided along with the Derivative Works; or, 119 | within a display generated by the Derivative Works, if and 120 | wherever such third-party notices normally appear. The contents 121 | of the NOTICE file are for informational purposes only and 122 | do not modify the License. You may add Your own attribution 123 | notices within Derivative Works that You distribute, alongside 124 | or as an addendum to the NOTICE text from the Work, provided 125 | that such additional attribution notices cannot be construed 126 | as modifying the License. 127 | 128 | You may add Your own copyright statement to Your modifications and 129 | may provide additional or different license terms and conditions 130 | for use, reproduction, or distribution of Your modifications, or 131 | for any such Derivative Works as a whole, provided Your use, 132 | reproduction, and distribution of the Work otherwise complies with 133 | the conditions stated in this License. 134 | 135 | 5. Submission of Contributions. Unless You explicitly state otherwise, 136 | any Contribution intentionally submitted for inclusion in the Work 137 | by You to the Licensor shall be under the terms and conditions of 138 | this License, without any additional terms or conditions. 139 | Notwithstanding the above, nothing herein shall supersede or modify 140 | the terms of any separate license agreement you may have executed 141 | with Licensor regarding such Contributions. 142 | 143 | 6. Trademarks. This License does not grant permission to use the trade 144 | names, trademarks, service marks, or product names of the Licensor, 145 | except as required for reasonable and customary use in describing the 146 | origin of the Work and reproducing the content of the NOTICE file. 147 | 148 | 7. Disclaimer of Warranty. Unless required by applicable law or 149 | agreed to in writing, Licensor provides the Work (and each 150 | Contributor provides its Contributions) on an "AS IS" BASIS, 151 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 152 | implied, including, without limitation, any warranties or conditions 153 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 154 | PARTICULAR PURPOSE. You are solely responsible for determining the 155 | appropriateness of using or redistributing the Work and assume any 156 | risks associated with Your exercise of permissions under this License. 157 | 158 | 8. Limitation of Liability. In no event and under no legal theory, 159 | whether in tort (including negligence), contract, or otherwise, 160 | unless required by applicable law (such as deliberate and grossly 161 | negligent acts) or agreed to in writing, shall any Contributor be 162 | liable to You for damages, including any direct, indirect, special, 163 | incidental, or consequential damages of any character arising as a 164 | result of this License or out of the use or inability to use the 165 | Work (including but not limited to damages for loss of goodwill, 166 | work stoppage, computer failure or malfunction, or any and all 167 | other commercial damages or losses), even if such Contributor 168 | has been advised of the possibility of such damages. 169 | 170 | 9. Accepting Warranty or Additional Liability. While redistributing 171 | the Work or Derivative Works thereof, You may choose to offer, 172 | and charge a fee for, acceptance of support, warranty, indemnity, 173 | or other liability obligations and/or rights consistent with this 174 | License. However, in accepting such obligations, You may act only 175 | on Your own behalf and on Your sole responsibility, not on behalf 176 | of any other Contributor, and only if You agree to indemnify, 177 | defend, and hold each Contributor harmless for any liability 178 | incurred by, or claims asserted against, such Contributor by reason 179 | of your accepting any such warranty or additional liability. 180 | 181 | END OF TERMS AND CONDITIONS 182 | 183 | Licensed under the Apache License, Version 2.0 (the "License"); 184 | you may not use this file except in compliance with the License. 185 | You may obtain a copy of the License at 186 | 187 | https://www.apache.org/licenses/LICENSE-2.0 188 | 189 | Unless required by applicable law or agreed to in writing, software 190 | distributed under the License is distributed on an "AS IS" BASIS, 191 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 192 | See the License for the specific language governing permissions and 193 | limitations under the License. 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `tendermint/go-p2p` 2 | 3 | [![CircleCI](https://circleci.com/gh/tendermint/go-p2p.svg?style=svg)](https://circleci.com/gh/tendermint/go-p2p) 4 | 5 | `tendermint/go-p2p` provides an abstraction around peer-to-peer communication.
6 | 7 | ## Peer/MConnection/Channel 8 | 9 | Each peer has one `MConnection` (multiplex connection) instance. 10 | 11 | __multiplex__ *noun* a system or signal involving simultaneous transmission of 12 | several messages along a single channel of communication. 13 | 14 | Each `MConnection` handles message transmission on multiple abstract communication 15 | `Channel`s. Each channel has a globally unique byte id. 16 | The byte id and the relative priorities of each `Channel` are configured upon 17 | initialization of the connection. 18 | 19 | There are two methods for sending messages: 20 | ```go 21 | func (m MConnection) Send(chID byte, msg interface{}) bool {} 22 | func (m MConnection) TrySend(chID byte, msg interface{}) bool {} 23 | ``` 24 | 25 | `Send(chID, msg)` is a blocking call that waits until `msg` is successfully queued 26 | for the channel with the given id byte `chID`. The message `msg` is serialized 27 | using the `tendermint/wire` submodule's `WriteBinary()` reflection routine. 28 | 29 | `TrySend(chID, msg)` is a nonblocking call that returns false if the channel's 30 | queue is full. 31 | 32 | `Send()` and `TrySend()` are also exposed for each `Peer`. 33 | 34 | ## Switch/Reactor 35 | 36 | The `Switch` handles peer connections and exposes an API to receive incoming messages 37 | on `Reactors`. Each `Reactor` is responsible for handling incoming messages of one 38 | or more `Channels`. So while sending outgoing messages is typically performed on the peer, 39 | incoming messages are received on the reactor. 40 | 41 | ```go 42 | // Declare a MyReactor reactor that handles messages on MyChannelID. 43 | type MyReactor struct{} 44 | 45 | func (reactor MyReactor) GetChannels() []*ChannelDescriptor { 46 | return []*ChannelDescriptor{ChannelDescriptor{ID:MyChannelID, Priority: 1}} 47 | } 48 | 49 | func (reactor MyReactor) Receive(chID byte, peer *Peer, msgBytes []byte) { 50 | r, n, err := bytes.NewBuffer(msgBytes), new(int64), new(error) 51 | msgString := ReadString(r, n, err) 52 | fmt.Println(msgString) 53 | } 54 | 55 | // Other Reactor methods omitted for brevity 56 | ... 57 | 58 | switch := NewSwitch([]Reactor{MyReactor{}}) 59 | 60 | ... 61 | 62 | // Send a random message to all outbound connections 63 | for _, peer := range switch.Peers().List() { 64 | if peer.IsOutbound() { 65 | peer.Send(MyChannelID, "Here's a random message") 66 | } 67 | } 68 | ``` 69 | 70 | ### PexReactor/AddrBook 71 | 72 | A `PEXReactor` reactor implementation is provided to automate peer discovery. 73 | 74 | ```go 75 | book := p2p.NewAddrBook(addrBookFilePath) 76 | pexReactor := p2p.NewPEXReactor(book) 77 | ... 78 | switch := NewSwitch([]Reactor{pexReactor, myReactor, ...}) 79 | ``` 80 | -------------------------------------------------------------------------------- /addrbook.go: -------------------------------------------------------------------------------- 1 | // Modified for Tendermint 2 | // Originally Copyright (c) 2013-2014 Conformal Systems LLC. 3 | // https://github.com/conformal/btcd/blob/master/LICENSE 4 | 5 | package p2p 6 | 7 | import ( 8 | "encoding/binary" 9 | "encoding/json" 10 | "math" 11 | "math/rand" 12 | "net" 13 | "os" 14 | "sync" 15 | "time" 16 | 17 | . "github.com/tendermint/go-common" 18 | crypto "github.com/tendermint/go-crypto" 19 | ) 20 | 21 | const ( 22 | // addresses under which the address manager will claim to need more addresses. 23 | needAddressThreshold = 1000 24 | 25 | // interval used to dump the address cache to disk for future use. 26 | dumpAddressInterval = time.Minute * 2 27 | 28 | // max addresses in each old address bucket. 29 | oldBucketSize = 64 30 | 31 | // buckets we split old addresses over. 32 | oldBucketCount = 64 33 | 34 | // max addresses in each new address bucket. 35 | newBucketSize = 64 36 | 37 | // buckets that we spread new addresses over. 38 | newBucketCount = 256 39 | 40 | // old buckets over which an address group will be spread. 41 | oldBucketsPerGroup = 4 42 | 43 | // new buckets over which an source address group will be spread. 44 | newBucketsPerGroup = 32 45 | 46 | // buckets a frequently seen new address may end up in. 47 | maxNewBucketsPerAddress = 4 48 | 49 | // days before which we assume an address has vanished 50 | // if we have not seen it announced in that long. 51 | numMissingDays = 30 52 | 53 | // tries without a single success before we assume an address is bad. 54 | numRetries = 3 55 | 56 | // max failures we will accept without a success before considering an address bad. 57 | maxFailures = 10 58 | 59 | // days since the last success before we will consider evicting an address. 60 | minBadDays = 7 61 | 62 | // % of total addresses known returned by GetSelection. 63 | getSelectionPercent = 23 64 | 65 | // min addresses that must be returned by GetSelection. Useful for bootstrapping. 66 | minGetSelection = 32 67 | 68 | // max addresses returned by GetSelection 69 | // NOTE: this must match "maxPexMessageSize" 70 | maxGetSelection = 250 71 | 72 | // current version of the on-disk format. 73 | serializationVersion = 1 74 | ) 75 | 76 | const ( 77 | bucketTypeNew = 0x01 78 | bucketTypeOld = 0x02 79 | ) 80 | 81 | // AddrBook - concurrency safe peer address manager. 82 | type AddrBook struct { 83 | BaseService 84 | 85 | mtx sync.Mutex 86 | filePath string 87 | routabilityStrict bool 88 | rand *rand.Rand 89 | key string 90 | ourAddrs map[string]*NetAddress 91 | addrLookup map[string]*knownAddress // new & old 92 | addrNew []map[string]*knownAddress 93 | addrOld []map[string]*knownAddress 94 | wg sync.WaitGroup 95 | nOld int 96 | nNew int 97 | } 98 | 99 | // NewAddrBook creates a new address book. 100 | // Use Start to begin processing asynchronous address updates. 101 | func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook { 102 | am := &AddrBook{ 103 | rand: rand.New(rand.NewSource(time.Now().UnixNano())), 104 | ourAddrs: make(map[string]*NetAddress), 105 | addrLookup: make(map[string]*knownAddress), 106 | filePath: filePath, 107 | routabilityStrict: routabilityStrict, 108 | } 109 | am.init() 110 | am.BaseService = *NewBaseService(log, "AddrBook", am) 111 | return am 112 | } 113 | 114 | // When modifying this, don't forget to update loadFromFile() 115 | func (a *AddrBook) init() { 116 | a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits 117 | // New addr buckets 118 | a.addrNew = make([]map[string]*knownAddress, newBucketCount) 119 | for i := range a.addrNew { 120 | a.addrNew[i] = make(map[string]*knownAddress) 121 | } 122 | // Old addr buckets 123 | a.addrOld = make([]map[string]*knownAddress, oldBucketCount) 124 | for i := range a.addrOld { 125 | a.addrOld[i] = make(map[string]*knownAddress) 126 | } 127 | } 128 | 129 | // OnStart implements Service. 130 | func (a *AddrBook) OnStart() error { 131 | a.BaseService.OnStart() 132 | a.loadFromFile(a.filePath) 133 | a.wg.Add(1) 134 | go a.saveRoutine() 135 | return nil 136 | } 137 | 138 | // OnStop implements Service. 139 | func (a *AddrBook) OnStop() { 140 | a.BaseService.OnStop() 141 | } 142 | 143 | func (a *AddrBook) Wait() { 144 | a.wg.Wait() 145 | } 146 | 147 | func (a *AddrBook) AddOurAddress(addr *NetAddress) { 148 | a.mtx.Lock() 149 | defer a.mtx.Unlock() 150 | log.Info("Add our address to book", "addr", addr) 151 | a.ourAddrs[addr.String()] = addr 152 | } 153 | 154 | func (a *AddrBook) OurAddresses() []*NetAddress { 155 | addrs := []*NetAddress{} 156 | for _, addr := range a.ourAddrs { 157 | addrs = append(addrs, addr) 158 | } 159 | return addrs 160 | } 161 | 162 | // NOTE: addr must not be nil 163 | func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) { 164 | a.mtx.Lock() 165 | defer a.mtx.Unlock() 166 | log.Info("Add address to book", "addr", addr, "src", src) 167 | a.addAddress(addr, src) 168 | } 169 | 170 | func (a *AddrBook) NeedMoreAddrs() bool { 171 | return a.Size() < needAddressThreshold 172 | } 173 | 174 | func (a *AddrBook) Size() int { 175 | a.mtx.Lock() 176 | defer a.mtx.Unlock() 177 | return a.size() 178 | } 179 | 180 | func (a *AddrBook) size() int { 181 | return a.nNew + a.nOld 182 | } 183 | 184 | // Pick an address to connect to with new/old bias. 185 | func (a *AddrBook) PickAddress(newBias int) *NetAddress { 186 | a.mtx.Lock() 187 | defer a.mtx.Unlock() 188 | 189 | if a.size() == 0 { 190 | return nil 191 | } 192 | if newBias > 100 { 193 | newBias = 100 194 | } 195 | if newBias < 0 { 196 | newBias = 0 197 | } 198 | 199 | // Bias between new and old addresses. 200 | oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(newBias)) 201 | newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias) 202 | 203 | if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation { 204 | // pick random Old bucket. 205 | var bucket map[string]*knownAddress = nil 206 | for len(bucket) == 0 { 207 | bucket = a.addrOld[a.rand.Intn(len(a.addrOld))] 208 | } 209 | // pick a random ka from bucket. 210 | randIndex := a.rand.Intn(len(bucket)) 211 | for _, ka := range bucket { 212 | if randIndex == 0 { 213 | return ka.Addr 214 | } 215 | randIndex-- 216 | } 217 | PanicSanity("Should not happen") 218 | } else { 219 | // pick random New bucket. 220 | var bucket map[string]*knownAddress = nil 221 | for len(bucket) == 0 { 222 | bucket = a.addrNew[a.rand.Intn(len(a.addrNew))] 223 | } 224 | // pick a random ka from bucket. 225 | randIndex := a.rand.Intn(len(bucket)) 226 | for _, ka := range bucket { 227 | if randIndex == 0 { 228 | return ka.Addr 229 | } 230 | randIndex-- 231 | } 232 | PanicSanity("Should not happen") 233 | } 234 | return nil 235 | } 236 | 237 | func (a *AddrBook) MarkGood(addr *NetAddress) { 238 | a.mtx.Lock() 239 | defer a.mtx.Unlock() 240 | ka := a.addrLookup[addr.String()] 241 | if ka == nil { 242 | return 243 | } 244 | ka.markGood() 245 | if ka.isNew() { 246 | a.moveToOld(ka) 247 | } 248 | } 249 | 250 | func (a *AddrBook) MarkAttempt(addr *NetAddress) { 251 | a.mtx.Lock() 252 | defer a.mtx.Unlock() 253 | ka := a.addrLookup[addr.String()] 254 | if ka == nil { 255 | return 256 | } 257 | ka.markAttempt() 258 | } 259 | 260 | // MarkBad currently just ejects the address. In the future, consider 261 | // blacklisting. 262 | func (a *AddrBook) MarkBad(addr *NetAddress) { 263 | a.RemoveAddress(addr) 264 | } 265 | 266 | // RemoveAddress removes the address from the book. 267 | func (a *AddrBook) RemoveAddress(addr *NetAddress) { 268 | a.mtx.Lock() 269 | defer a.mtx.Unlock() 270 | ka := a.addrLookup[addr.String()] 271 | if ka == nil { 272 | return 273 | } 274 | log.Info("Remove address from book", "addr", addr) 275 | a.removeFromAllBuckets(ka) 276 | } 277 | 278 | /* Peer exchange */ 279 | 280 | // GetSelection randomly selects some addresses (old & new). Suitable for peer-exchange protocols. 281 | func (a *AddrBook) GetSelection() []*NetAddress { 282 | a.mtx.Lock() 283 | defer a.mtx.Unlock() 284 | 285 | if a.size() == 0 { 286 | return nil 287 | } 288 | 289 | allAddr := make([]*NetAddress, a.size()) 290 | i := 0 291 | for _, v := range a.addrLookup { 292 | allAddr[i] = v.Addr 293 | i++ 294 | } 295 | 296 | numAddresses := MaxInt( 297 | MinInt(minGetSelection, len(allAddr)), 298 | len(allAddr)*getSelectionPercent/100) 299 | numAddresses = MinInt(maxGetSelection, numAddresses) 300 | 301 | // Fisher-Yates shuffle the array. We only need to do the first 302 | // `numAddresses' since we are throwing the rest. 303 | for i := 0; i < numAddresses; i++ { 304 | // pick a number between current index and the end 305 | j := rand.Intn(len(allAddr)-i) + i 306 | allAddr[i], allAddr[j] = allAddr[j], allAddr[i] 307 | } 308 | 309 | // slice off the limit we are willing to share. 310 | return allAddr[:numAddresses] 311 | } 312 | 313 | /* Loading & Saving */ 314 | 315 | type addrBookJSON struct { 316 | Key string 317 | Addrs []*knownAddress 318 | } 319 | 320 | func (a *AddrBook) saveToFile(filePath string) { 321 | log.Info("Saving AddrBook to file", "size", a.Size()) 322 | 323 | a.mtx.Lock() 324 | defer a.mtx.Unlock() 325 | // Compile Addrs 326 | addrs := []*knownAddress{} 327 | for _, ka := range a.addrLookup { 328 | addrs = append(addrs, ka) 329 | } 330 | 331 | aJSON := &addrBookJSON{ 332 | Key: a.key, 333 | Addrs: addrs, 334 | } 335 | 336 | jsonBytes, err := json.MarshalIndent(aJSON, "", "\t") 337 | if err != nil { 338 | log.Error("Failed to save AddrBook to file", "err", err) 339 | return 340 | } 341 | err = WriteFileAtomic(filePath, jsonBytes, 0644) 342 | if err != nil { 343 | log.Error("Failed to save AddrBook to file", "file", filePath, "error", err) 344 | } 345 | } 346 | 347 | // Returns false if file does not exist. 348 | // Panics if file is corrupt. 349 | func (a *AddrBook) loadFromFile(filePath string) bool { 350 | // If doesn't exist, do nothing. 351 | _, err := os.Stat(filePath) 352 | if os.IsNotExist(err) { 353 | return false 354 | } 355 | 356 | // Load addrBookJSON{} 357 | r, err := os.Open(filePath) 358 | if err != nil { 359 | PanicCrisis(Fmt("Error opening file %s: %v", filePath, err)) 360 | } 361 | defer r.Close() 362 | aJSON := &addrBookJSON{} 363 | dec := json.NewDecoder(r) 364 | err = dec.Decode(aJSON) 365 | if err != nil { 366 | PanicCrisis(Fmt("Error reading file %s: %v", filePath, err)) 367 | } 368 | 369 | // Restore all the fields... 370 | // Restore the key 371 | a.key = aJSON.Key 372 | // Restore .addrNew & .addrOld 373 | for _, ka := range aJSON.Addrs { 374 | for _, bucketIndex := range ka.Buckets { 375 | bucket := a.getBucket(ka.BucketType, bucketIndex) 376 | bucket[ka.Addr.String()] = ka 377 | } 378 | a.addrLookup[ka.Addr.String()] = ka 379 | if ka.BucketType == bucketTypeNew { 380 | a.nNew++ 381 | } else { 382 | a.nOld++ 383 | } 384 | } 385 | return true 386 | } 387 | 388 | // Save saves the book. 389 | func (a *AddrBook) Save() { 390 | log.Info("Saving AddrBook to file", "size", a.Size()) 391 | a.saveToFile(a.filePath) 392 | } 393 | 394 | /* Private methods */ 395 | 396 | func (a *AddrBook) saveRoutine() { 397 | dumpAddressTicker := time.NewTicker(dumpAddressInterval) 398 | out: 399 | for { 400 | select { 401 | case <-dumpAddressTicker.C: 402 | a.saveToFile(a.filePath) 403 | case <-a.Quit: 404 | break out 405 | } 406 | } 407 | dumpAddressTicker.Stop() 408 | a.saveToFile(a.filePath) 409 | a.wg.Done() 410 | log.Notice("Address handler done") 411 | } 412 | 413 | func (a *AddrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress { 414 | switch bucketType { 415 | case bucketTypeNew: 416 | return a.addrNew[bucketIdx] 417 | case bucketTypeOld: 418 | return a.addrOld[bucketIdx] 419 | default: 420 | PanicSanity("Should not happen") 421 | return nil 422 | } 423 | } 424 | 425 | // Adds ka to new bucket. Returns false if it couldn't do it cuz buckets full. 426 | // NOTE: currently it always returns true. 427 | func (a *AddrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { 428 | // Sanity check 429 | if ka.isOld() { 430 | log.Warn(Fmt("Cannot add address already in old bucket to a new bucket: %v", ka)) 431 | return false 432 | } 433 | 434 | addrStr := ka.Addr.String() 435 | bucket := a.getBucket(bucketTypeNew, bucketIdx) 436 | 437 | // Already exists? 438 | if _, ok := bucket[addrStr]; ok { 439 | return true 440 | } 441 | 442 | // Enforce max addresses. 443 | if len(bucket) > newBucketSize { 444 | log.Notice("new bucket is full, expiring old ") 445 | a.expireNew(bucketIdx) 446 | } 447 | 448 | // Add to bucket. 449 | bucket[addrStr] = ka 450 | if ka.addBucketRef(bucketIdx) == 1 { 451 | a.nNew++ 452 | } 453 | 454 | // Ensure in addrLookup 455 | a.addrLookup[addrStr] = ka 456 | 457 | return true 458 | } 459 | 460 | // Adds ka to old bucket. Returns false if it couldn't do it cuz buckets full. 461 | func (a *AddrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool { 462 | // Sanity check 463 | if ka.isNew() { 464 | log.Warn(Fmt("Cannot add new address to old bucket: %v", ka)) 465 | return false 466 | } 467 | if len(ka.Buckets) != 0 { 468 | log.Warn(Fmt("Cannot add already old address to another old bucket: %v", ka)) 469 | return false 470 | } 471 | 472 | addrStr := ka.Addr.String() 473 | bucket := a.getBucket(bucketTypeNew, bucketIdx) 474 | 475 | // Already exists? 476 | if _, ok := bucket[addrStr]; ok { 477 | return true 478 | } 479 | 480 | // Enforce max addresses. 481 | if len(bucket) > oldBucketSize { 482 | return false 483 | } 484 | 485 | // Add to bucket. 486 | bucket[addrStr] = ka 487 | if ka.addBucketRef(bucketIdx) == 1 { 488 | a.nOld++ 489 | } 490 | 491 | // Ensure in addrLookup 492 | a.addrLookup[addrStr] = ka 493 | 494 | return true 495 | } 496 | 497 | func (a *AddrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx int) { 498 | if ka.BucketType != bucketType { 499 | log.Warn(Fmt("Bucket type mismatch: %v", ka)) 500 | return 501 | } 502 | bucket := a.getBucket(bucketType, bucketIdx) 503 | delete(bucket, ka.Addr.String()) 504 | if ka.removeBucketRef(bucketIdx) == 0 { 505 | if bucketType == bucketTypeNew { 506 | a.nNew-- 507 | } else { 508 | a.nOld-- 509 | } 510 | delete(a.addrLookup, ka.Addr.String()) 511 | } 512 | } 513 | 514 | func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) { 515 | for _, bucketIdx := range ka.Buckets { 516 | bucket := a.getBucket(ka.BucketType, bucketIdx) 517 | delete(bucket, ka.Addr.String()) 518 | } 519 | ka.Buckets = nil 520 | if ka.BucketType == bucketTypeNew { 521 | a.nNew-- 522 | } else { 523 | a.nOld-- 524 | } 525 | delete(a.addrLookup, ka.Addr.String()) 526 | } 527 | 528 | func (a *AddrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { 529 | bucket := a.getBucket(bucketType, bucketIdx) 530 | var oldest *knownAddress 531 | for _, ka := range bucket { 532 | if oldest == nil || ka.LastAttempt.Before(oldest.LastAttempt) { 533 | oldest = ka 534 | } 535 | } 536 | return oldest 537 | } 538 | 539 | func (a *AddrBook) addAddress(addr, src *NetAddress) { 540 | if a.routabilityStrict && !addr.Routable() { 541 | log.Warn(Fmt("Cannot add non-routable address %v", addr)) 542 | return 543 | } 544 | if _, ok := a.ourAddrs[addr.String()]; ok { 545 | // Ignore our own listener address. 546 | return 547 | } 548 | 549 | ka := a.addrLookup[addr.String()] 550 | 551 | if ka != nil { 552 | // Already old. 553 | if ka.isOld() { 554 | return 555 | } 556 | // Already in max new buckets. 557 | if len(ka.Buckets) == maxNewBucketsPerAddress { 558 | return 559 | } 560 | // The more entries we have, the less likely we are to add more. 561 | factor := int32(2 * len(ka.Buckets)) 562 | if a.rand.Int31n(factor) != 0 { 563 | return 564 | } 565 | } else { 566 | ka = newKnownAddress(addr, src) 567 | } 568 | 569 | bucket := a.calcNewBucket(addr, src) 570 | a.addToNewBucket(ka, bucket) 571 | 572 | log.Notice("Added new address", "address", addr, "total", a.size()) 573 | } 574 | 575 | // Make space in the new buckets by expiring the really bad entries. 576 | // If no bad entries are available we remove the oldest. 577 | func (a *AddrBook) expireNew(bucketIdx int) { 578 | for addrStr, ka := range a.addrNew[bucketIdx] { 579 | // If an entry is bad, throw it away 580 | if ka.isBad() { 581 | log.Notice(Fmt("expiring bad address %v", addrStr)) 582 | a.removeFromBucket(ka, bucketTypeNew, bucketIdx) 583 | return 584 | } 585 | } 586 | 587 | // If we haven't thrown out a bad entry, throw out the oldest entry 588 | oldest := a.pickOldest(bucketTypeNew, bucketIdx) 589 | a.removeFromBucket(oldest, bucketTypeNew, bucketIdx) 590 | } 591 | 592 | // Promotes an address from new to old. 593 | // TODO: Move to old probabilistically. 594 | // The better a node is, the less likely it should be evicted from an old bucket. 595 | func (a *AddrBook) moveToOld(ka *knownAddress) { 596 | // Sanity check 597 | if ka.isOld() { 598 | log.Warn(Fmt("Cannot promote address that is already old %v", ka)) 599 | return 600 | } 601 | if len(ka.Buckets) == 0 { 602 | log.Warn(Fmt("Cannot promote address that isn't in any new buckets %v", ka)) 603 | return 604 | } 605 | 606 | // Remember one of the buckets in which ka is in. 607 | freedBucket := ka.Buckets[0] 608 | // Remove from all (new) buckets. 609 | a.removeFromAllBuckets(ka) 610 | // It's officially old now. 611 | ka.BucketType = bucketTypeOld 612 | 613 | // Try to add it to its oldBucket destination. 614 | oldBucketIdx := a.calcOldBucket(ka.Addr) 615 | added := a.addToOldBucket(ka, oldBucketIdx) 616 | if !added { 617 | // No room, must evict something 618 | oldest := a.pickOldest(bucketTypeOld, oldBucketIdx) 619 | a.removeFromBucket(oldest, bucketTypeOld, oldBucketIdx) 620 | // Find new bucket to put oldest in 621 | newBucketIdx := a.calcNewBucket(oldest.Addr, oldest.Src) 622 | added := a.addToNewBucket(oldest, newBucketIdx) 623 | // No space in newBucket either, just put it in freedBucket from above. 624 | if !added { 625 | added := a.addToNewBucket(oldest, freedBucket) 626 | if !added { 627 | log.Warn(Fmt("Could not migrate oldest %v to freedBucket %v", oldest, freedBucket)) 628 | } 629 | } 630 | // Finally, add to bucket again. 631 | added = a.addToOldBucket(ka, oldBucketIdx) 632 | if !added { 633 | log.Warn(Fmt("Could not re-add ka %v to oldBucketIdx %v", ka, oldBucketIdx)) 634 | } 635 | } 636 | } 637 | 638 | // doublesha256( key + sourcegroup + 639 | // int64(doublesha256(key + group + sourcegroup))%bucket_per_group ) % num_new_buckets 640 | func (a *AddrBook) calcNewBucket(addr, src *NetAddress) int { 641 | data1 := []byte{} 642 | data1 = append(data1, []byte(a.key)...) 643 | data1 = append(data1, []byte(a.groupKey(addr))...) 644 | data1 = append(data1, []byte(a.groupKey(src))...) 645 | hash1 := doubleSha256(data1) 646 | hash64 := binary.BigEndian.Uint64(hash1) 647 | hash64 %= newBucketsPerGroup 648 | var hashbuf [8]byte 649 | binary.BigEndian.PutUint64(hashbuf[:], hash64) 650 | data2 := []byte{} 651 | data2 = append(data2, []byte(a.key)...) 652 | data2 = append(data2, a.groupKey(src)...) 653 | data2 = append(data2, hashbuf[:]...) 654 | 655 | hash2 := doubleSha256(data2) 656 | return int(binary.BigEndian.Uint64(hash2) % newBucketCount) 657 | } 658 | 659 | // doublesha256( key + group + 660 | // int64(doublesha256(key + addr))%buckets_per_group ) % num_old_buckets 661 | func (a *AddrBook) calcOldBucket(addr *NetAddress) int { 662 | data1 := []byte{} 663 | data1 = append(data1, []byte(a.key)...) 664 | data1 = append(data1, []byte(addr.String())...) 665 | hash1 := doubleSha256(data1) 666 | hash64 := binary.BigEndian.Uint64(hash1) 667 | hash64 %= oldBucketsPerGroup 668 | var hashbuf [8]byte 669 | binary.BigEndian.PutUint64(hashbuf[:], hash64) 670 | data2 := []byte{} 671 | data2 = append(data2, []byte(a.key)...) 672 | data2 = append(data2, a.groupKey(addr)...) 673 | data2 = append(data2, hashbuf[:]...) 674 | 675 | hash2 := doubleSha256(data2) 676 | return int(binary.BigEndian.Uint64(hash2) % oldBucketCount) 677 | } 678 | 679 | // Return a string representing the network group of this address. 680 | // This is the /16 for IPv6, the /32 (/36 for he.net) for IPv6, the string 681 | // "local" for a local address and the string "unroutable for an unroutable 682 | // address. 683 | func (a *AddrBook) groupKey(na *NetAddress) string { 684 | if a.routabilityStrict && na.Local() { 685 | return "local" 686 | } 687 | if a.routabilityStrict && !na.Routable() { 688 | return "unroutable" 689 | } 690 | 691 | if ipv4 := na.IP.To4(); ipv4 != nil { 692 | return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(16, 32)}).String() 693 | } 694 | if na.RFC6145() || na.RFC6052() { 695 | // last four bytes are the ip address 696 | ip := net.IP(na.IP[12:16]) 697 | return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String() 698 | } 699 | 700 | if na.RFC3964() { 701 | ip := net.IP(na.IP[2:7]) 702 | return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String() 703 | 704 | } 705 | if na.RFC4380() { 706 | // teredo tunnels have the last 4 bytes as the v4 address XOR 707 | // 0xff. 708 | ip := net.IP(make([]byte, 4)) 709 | for i, byte := range na.IP[12:16] { 710 | ip[i] = byte ^ 0xff 711 | } 712 | return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String() 713 | } 714 | 715 | // OK, so now we know ourselves to be a IPv6 address. 716 | // bitcoind uses /32 for everything, except for Hurricane Electric's 717 | // (he.net) IP range, which it uses /36 for. 718 | bits := 32 719 | heNet := &net.IPNet{IP: net.ParseIP("2001:470::"), 720 | Mask: net.CIDRMask(32, 128)} 721 | if heNet.Contains(na.IP) { 722 | bits = 36 723 | } 724 | 725 | return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(bits, 128)}).String() 726 | } 727 | 728 | //----------------------------------------------------------------------------- 729 | 730 | /* 731 | knownAddress 732 | 733 | tracks information about a known network address that is used 734 | to determine how viable an address is. 735 | */ 736 | type knownAddress struct { 737 | Addr *NetAddress 738 | Src *NetAddress 739 | Attempts int32 740 | LastAttempt time.Time 741 | LastSuccess time.Time 742 | BucketType byte 743 | Buckets []int 744 | } 745 | 746 | func newKnownAddress(addr *NetAddress, src *NetAddress) *knownAddress { 747 | return &knownAddress{ 748 | Addr: addr, 749 | Src: src, 750 | Attempts: 0, 751 | LastAttempt: time.Now(), 752 | BucketType: bucketTypeNew, 753 | Buckets: nil, 754 | } 755 | } 756 | 757 | func (ka *knownAddress) isOld() bool { 758 | return ka.BucketType == bucketTypeOld 759 | } 760 | 761 | func (ka *knownAddress) isNew() bool { 762 | return ka.BucketType == bucketTypeNew 763 | } 764 | 765 | func (ka *knownAddress) markAttempt() { 766 | now := time.Now() 767 | ka.LastAttempt = now 768 | ka.Attempts += 1 769 | } 770 | 771 | func (ka *knownAddress) markGood() { 772 | now := time.Now() 773 | ka.LastAttempt = now 774 | ka.Attempts = 0 775 | ka.LastSuccess = now 776 | } 777 | 778 | func (ka *knownAddress) addBucketRef(bucketIdx int) int { 779 | for _, bucket := range ka.Buckets { 780 | if bucket == bucketIdx { 781 | log.Warn(Fmt("Bucket already exists in ka.Buckets: %v", ka)) 782 | return -1 783 | } 784 | } 785 | ka.Buckets = append(ka.Buckets, bucketIdx) 786 | return len(ka.Buckets) 787 | } 788 | 789 | func (ka *knownAddress) removeBucketRef(bucketIdx int) int { 790 | buckets := []int{} 791 | for _, bucket := range ka.Buckets { 792 | if bucket != bucketIdx { 793 | buckets = append(buckets, bucket) 794 | } 795 | } 796 | if len(buckets) != len(ka.Buckets)-1 { 797 | log.Warn(Fmt("bucketIdx not found in ka.Buckets: %v", ka)) 798 | return -1 799 | } 800 | ka.Buckets = buckets 801 | return len(ka.Buckets) 802 | } 803 | 804 | /* 805 | An address is bad if the address in question has not been tried in the last 806 | minute and meets one of the following criteria: 807 | 808 | 1) It claims to be from the future 809 | 2) It hasn't been seen in over a month 810 | 3) It has failed at least three times and never succeeded 811 | 4) It has failed ten times in the last week 812 | 813 | All addresses that meet these criteria are assumed to be worthless and not 814 | worth keeping hold of. 815 | */ 816 | func (ka *knownAddress) isBad() bool { 817 | // Has been attempted in the last minute --> good 818 | if ka.LastAttempt.Before(time.Now().Add(-1 * time.Minute)) { 819 | return false 820 | } 821 | 822 | // Over a month old? 823 | if ka.LastAttempt.After(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) { 824 | return true 825 | } 826 | 827 | // Never succeeded? 828 | if ka.LastSuccess.IsZero() && ka.Attempts >= numRetries { 829 | return true 830 | } 831 | 832 | // Hasn't succeeded in too long? 833 | if ka.LastSuccess.Before(time.Now().Add(-1*minBadDays*time.Hour*24)) && 834 | ka.Attempts >= maxFailures { 835 | return true 836 | } 837 | 838 | return false 839 | } 840 | -------------------------------------------------------------------------------- /addrbook_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "math/rand" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func createTempFileName(prefix string) string { 13 | f, err := ioutil.TempFile("", prefix) 14 | if err != nil { 15 | panic(err) 16 | } 17 | fname := f.Name() 18 | err = f.Close() 19 | if err != nil { 20 | panic(err) 21 | } 22 | return fname 23 | } 24 | 25 | func TestAddrBookSaveLoad(t *testing.T) { 26 | fname := createTempFileName("addrbook_test") 27 | 28 | // 0 addresses 29 | book := NewAddrBook(fname, true) 30 | book.saveToFile(fname) 31 | 32 | book = NewAddrBook(fname, true) 33 | book.loadFromFile(fname) 34 | 35 | assert.Zero(t, book.Size()) 36 | 37 | // 100 addresses 38 | randAddrs := randNetAddressPairs(t, 100) 39 | 40 | for _, addrSrc := range randAddrs { 41 | book.AddAddress(addrSrc.addr, addrSrc.src) 42 | } 43 | 44 | assert.Equal(t, 100, book.Size()) 45 | book.saveToFile(fname) 46 | 47 | book = NewAddrBook(fname, true) 48 | book.loadFromFile(fname) 49 | 50 | assert.Equal(t, 100, book.Size()) 51 | } 52 | 53 | func TestAddrBookLookup(t *testing.T) { 54 | fname := createTempFileName("addrbook_test") 55 | 56 | randAddrs := randNetAddressPairs(t, 100) 57 | 58 | book := NewAddrBook(fname, true) 59 | for _, addrSrc := range randAddrs { 60 | addr := addrSrc.addr 61 | src := addrSrc.src 62 | book.AddAddress(addr, src) 63 | 64 | ka := book.addrLookup[addr.String()] 65 | assert.NotNil(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr) 66 | 67 | if !(ka.Addr.Equals(addr) && ka.Src.Equals(src)) { 68 | t.Fatalf("KnownAddress doesn't match addr & src") 69 | } 70 | } 71 | } 72 | 73 | func TestAddrBookPromoteToOld(t *testing.T) { 74 | fname := createTempFileName("addrbook_test") 75 | 76 | randAddrs := randNetAddressPairs(t, 100) 77 | 78 | book := NewAddrBook(fname, true) 79 | for _, addrSrc := range randAddrs { 80 | book.AddAddress(addrSrc.addr, addrSrc.src) 81 | } 82 | 83 | // Attempt all addresses. 84 | for _, addrSrc := range randAddrs { 85 | book.MarkAttempt(addrSrc.addr) 86 | } 87 | 88 | // Promote half of them 89 | for i, addrSrc := range randAddrs { 90 | if i%2 == 0 { 91 | book.MarkGood(addrSrc.addr) 92 | } 93 | } 94 | 95 | // TODO: do more testing :) 96 | 97 | selection := book.GetSelection() 98 | t.Logf("selection: %v", selection) 99 | 100 | if len(selection) > book.Size() { 101 | t.Errorf("selection could not be bigger than the book") 102 | } 103 | } 104 | 105 | func TestAddrBookHandlesDuplicates(t *testing.T) { 106 | fname := createTempFileName("addrbook_test") 107 | 108 | book := NewAddrBook(fname, true) 109 | 110 | randAddrs := randNetAddressPairs(t, 100) 111 | 112 | differentSrc := randIPv4Address(t) 113 | for _, addrSrc := range randAddrs { 114 | book.AddAddress(addrSrc.addr, addrSrc.src) 115 | book.AddAddress(addrSrc.addr, addrSrc.src) // duplicate 116 | book.AddAddress(addrSrc.addr, differentSrc) // different src 117 | } 118 | 119 | assert.Equal(t, 100, book.Size()) 120 | } 121 | 122 | type netAddressPair struct { 123 | addr *NetAddress 124 | src *NetAddress 125 | } 126 | 127 | func randNetAddressPairs(t *testing.T, n int) []netAddressPair { 128 | randAddrs := make([]netAddressPair, n) 129 | for i := 0; i < n; i++ { 130 | randAddrs[i] = netAddressPair{addr: randIPv4Address(t), src: randIPv4Address(t)} 131 | } 132 | return randAddrs 133 | } 134 | 135 | func randIPv4Address(t *testing.T) *NetAddress { 136 | for { 137 | ip := fmt.Sprintf("%v.%v.%v.%v", 138 | rand.Intn(254)+1, 139 | rand.Intn(255), 140 | rand.Intn(255), 141 | rand.Intn(255), 142 | ) 143 | port := rand.Intn(65535-1) + 1 144 | addr, err := NewNetAddressString(fmt.Sprintf("%v:%v", ip, port)) 145 | assert.Nil(t, err, "error generating rand network address") 146 | if addr.Routable() { 147 | return addr 148 | } 149 | } 150 | } 151 | 152 | func TestAddrBookRemoveAddress(t *testing.T) { 153 | fname := createTempFileName("addrbook_test") 154 | book := NewAddrBook(fname, true) 155 | 156 | addr := randIPv4Address(t) 157 | book.AddAddress(addr, addr) 158 | assert.Equal(t, 1, book.Size()) 159 | 160 | book.RemoveAddress(addr) 161 | assert.Equal(t, 0, book.Size()) 162 | 163 | nonExistingAddr := randIPv4Address(t) 164 | book.RemoveAddress(nonExistingAddr) 165 | assert.Equal(t, 0, book.Size()) 166 | } 167 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | cfg "github.com/tendermint/go-config" 5 | ) 6 | 7 | const ( 8 | // Switch config keys 9 | configKeyDialTimeoutSeconds = "dial_timeout_seconds" 10 | configKeyHandshakeTimeoutSeconds = "handshake_timeout_seconds" 11 | configKeyMaxNumPeers = "max_num_peers" 12 | configKeyAuthEnc = "authenticated_encryption" 13 | 14 | // MConnection config keys 15 | configKeySendRate = "send_rate" 16 | configKeyRecvRate = "recv_rate" 17 | 18 | // Fuzz params 19 | configFuzzEnable = "fuzz_enable" // use the fuzz wrapped conn 20 | configFuzzMode = "fuzz_mode" // eg. drop, delay 21 | configFuzzMaxDelayMilliseconds = "fuzz_max_delay_milliseconds" 22 | configFuzzProbDropRW = "fuzz_prob_drop_rw" 23 | configFuzzProbDropConn = "fuzz_prob_drop_conn" 24 | configFuzzProbSleep = "fuzz_prob_sleep" 25 | ) 26 | 27 | func setConfigDefaults(config cfg.Config) { 28 | // Switch default config 29 | config.SetDefault(configKeyDialTimeoutSeconds, 3) 30 | config.SetDefault(configKeyHandshakeTimeoutSeconds, 20) 31 | config.SetDefault(configKeyMaxNumPeers, 50) 32 | config.SetDefault(configKeyAuthEnc, true) 33 | 34 | // MConnection default config 35 | config.SetDefault(configKeySendRate, 512000) // 500KB/s 36 | config.SetDefault(configKeyRecvRate, 512000) // 500KB/s 37 | 38 | // Fuzz defaults 39 | config.SetDefault(configFuzzEnable, false) 40 | config.SetDefault(configFuzzMode, FuzzModeDrop) 41 | config.SetDefault(configFuzzMaxDelayMilliseconds, 3000) 42 | config.SetDefault(configFuzzProbDropRW, 0.2) 43 | config.SetDefault(configFuzzProbDropConn, 0.00) 44 | config.SetDefault(configFuzzProbSleep, 0.00) 45 | } 46 | -------------------------------------------------------------------------------- /connection.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "math" 8 | "net" 9 | "runtime/debug" 10 | "sync/atomic" 11 | "time" 12 | 13 | cmn "github.com/tendermint/go-common" 14 | flow "github.com/tendermint/go-flowrate/flowrate" 15 | wire "github.com/tendermint/go-wire" 16 | ) 17 | 18 | const ( 19 | numBatchMsgPackets = 10 20 | minReadBufferSize = 1024 21 | minWriteBufferSize = 65536 22 | updateState = 2 * time.Second 23 | pingTimeout = 40 * time.Second 24 | flushThrottle = 100 * time.Millisecond 25 | 26 | defaultSendQueueCapacity = 1 27 | defaultSendRate = int64(512000) // 500KB/s 28 | defaultRecvBufferCapacity = 4096 29 | defaultRecvMessageCapacity = 22020096 // 21MB 30 | defaultRecvRate = int64(512000) // 500KB/s 31 | defaultSendTimeout = 10 * time.Second 32 | ) 33 | 34 | type receiveCbFunc func(chID byte, msgBytes []byte) 35 | type errorCbFunc func(interface{}) 36 | 37 | /* 38 | Each peer has one `MConnection` (multiplex connection) instance. 39 | 40 | __multiplex__ *noun* a system or signal involving simultaneous transmission of 41 | several messages along a single channel of communication. 42 | 43 | Each `MConnection` handles message transmission on multiple abstract communication 44 | `Channel`s. Each channel has a globally unique byte id. 45 | The byte id and the relative priorities of each `Channel` are configured upon 46 | initialization of the connection. 47 | 48 | There are two methods for sending messages: 49 | func (m MConnection) Send(chID byte, msg interface{}) bool {} 50 | func (m MConnection) TrySend(chID byte, msg interface{}) bool {} 51 | 52 | `Send(chID, msg)` is a blocking call that waits until `msg` is successfully queued 53 | for the channel with the given id byte `chID`, or until the request times out. 54 | The message `msg` is serialized using the `tendermint/wire` submodule's 55 | `WriteBinary()` reflection routine. 56 | 57 | `TrySend(chID, msg)` is a nonblocking call that returns false if the channel's 58 | queue is full. 59 | 60 | Inbound message bytes are handled with an onReceive callback function. 61 | */ 62 | type MConnection struct { 63 | cmn.BaseService 64 | 65 | conn net.Conn 66 | bufReader *bufio.Reader 67 | bufWriter *bufio.Writer 68 | sendMonitor *flow.Monitor 69 | recvMonitor *flow.Monitor 70 | send chan struct{} 71 | pong chan struct{} 72 | channels []*Channel 73 | channelsIdx map[byte]*Channel 74 | onReceive receiveCbFunc 75 | onError errorCbFunc 76 | errored uint32 77 | config *MConnConfig 78 | 79 | quit chan struct{} 80 | flushTimer *cmn.ThrottleTimer // flush writes as necessary but throttled. 81 | pingTimer *cmn.RepeatTimer // send pings periodically 82 | chStatsTimer *cmn.RepeatTimer // update channel stats periodically 83 | 84 | LocalAddress *NetAddress 85 | RemoteAddress *NetAddress 86 | } 87 | 88 | // MConnConfig is a MConnection configuration. 89 | type MConnConfig struct { 90 | SendRate int64 91 | RecvRate int64 92 | } 93 | 94 | // DefaultMConnConfig returns the default config. 95 | func DefaultMConnConfig() *MConnConfig { 96 | return &MConnConfig{ 97 | SendRate: defaultSendRate, 98 | RecvRate: defaultRecvRate, 99 | } 100 | } 101 | 102 | // NewMConnection wraps net.Conn and creates multiplex connection 103 | func NewMConnection(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc) *MConnection { 104 | return NewMConnectionWithConfig( 105 | conn, 106 | chDescs, 107 | onReceive, 108 | onError, 109 | DefaultMConnConfig()) 110 | } 111 | 112 | // NewMConnectionWithConfig wraps net.Conn and creates multiplex connection with a config 113 | func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc, config *MConnConfig) *MConnection { 114 | mconn := &MConnection{ 115 | conn: conn, 116 | bufReader: bufio.NewReaderSize(conn, minReadBufferSize), 117 | bufWriter: bufio.NewWriterSize(conn, minWriteBufferSize), 118 | sendMonitor: flow.New(0, 0), 119 | recvMonitor: flow.New(0, 0), 120 | send: make(chan struct{}, 1), 121 | pong: make(chan struct{}), 122 | onReceive: onReceive, 123 | onError: onError, 124 | config: config, 125 | 126 | LocalAddress: NewNetAddress(conn.LocalAddr()), 127 | RemoteAddress: NewNetAddress(conn.RemoteAddr()), 128 | } 129 | 130 | // Create channels 131 | var channelsIdx = map[byte]*Channel{} 132 | var channels = []*Channel{} 133 | 134 | for _, desc := range chDescs { 135 | descCopy := *desc // copy the desc else unsafe access across connections 136 | channel := newChannel(mconn, &descCopy) 137 | channelsIdx[channel.id] = channel 138 | channels = append(channels, channel) 139 | } 140 | mconn.channels = channels 141 | mconn.channelsIdx = channelsIdx 142 | 143 | mconn.BaseService = *cmn.NewBaseService(log, "MConnection", mconn) 144 | 145 | return mconn 146 | } 147 | 148 | func (c *MConnection) OnStart() error { 149 | c.BaseService.OnStart() 150 | c.quit = make(chan struct{}) 151 | c.flushTimer = cmn.NewThrottleTimer("flush", flushThrottle) 152 | c.pingTimer = cmn.NewRepeatTimer("ping", pingTimeout) 153 | c.chStatsTimer = cmn.NewRepeatTimer("chStats", updateState) 154 | go c.sendRoutine() 155 | go c.recvRoutine() 156 | return nil 157 | } 158 | 159 | func (c *MConnection) OnStop() { 160 | c.BaseService.OnStop() 161 | c.flushTimer.Stop() 162 | c.pingTimer.Stop() 163 | c.chStatsTimer.Stop() 164 | if c.quit != nil { 165 | close(c.quit) 166 | } 167 | c.conn.Close() 168 | // We can't close pong safely here because 169 | // recvRoutine may write to it after we've stopped. 170 | // Though it doesn't need to get closed at all, 171 | // we close it @ recvRoutine. 172 | // close(c.pong) 173 | } 174 | 175 | func (c *MConnection) String() string { 176 | return fmt.Sprintf("MConn{%v}", c.conn.RemoteAddr()) 177 | } 178 | 179 | func (c *MConnection) flush() { 180 | log.Debug("Flush", "conn", c) 181 | err := c.bufWriter.Flush() 182 | if err != nil { 183 | log.Warn("MConnection flush failed", "error", err) 184 | } 185 | } 186 | 187 | // Catch panics, usually caused by remote disconnects. 188 | func (c *MConnection) _recover() { 189 | if r := recover(); r != nil { 190 | stack := debug.Stack() 191 | err := cmn.StackError{r, stack} 192 | c.stopForError(err) 193 | } 194 | } 195 | 196 | func (c *MConnection) stopForError(r interface{}) { 197 | c.Stop() 198 | if atomic.CompareAndSwapUint32(&c.errored, 0, 1) { 199 | if c.onError != nil { 200 | c.onError(r) 201 | } 202 | } 203 | } 204 | 205 | // Queues a message to be sent to channel. 206 | func (c *MConnection) Send(chID byte, msg interface{}) bool { 207 | if !c.IsRunning() { 208 | return false 209 | } 210 | 211 | log.Debug("Send", "channel", chID, "conn", c, "msg", msg) //, "bytes", wire.BinaryBytes(msg)) 212 | 213 | // Send message to channel. 214 | channel, ok := c.channelsIdx[chID] 215 | if !ok { 216 | log.Error(cmn.Fmt("Cannot send bytes, unknown channel %X", chID)) 217 | return false 218 | } 219 | 220 | success := channel.sendBytes(wire.BinaryBytes(msg)) 221 | if success { 222 | // Wake up sendRoutine if necessary 223 | select { 224 | case c.send <- struct{}{}: 225 | default: 226 | } 227 | } else { 228 | log.Warn("Send failed", "channel", chID, "conn", c, "msg", msg) 229 | } 230 | return success 231 | } 232 | 233 | // Queues a message to be sent to channel. 234 | // Nonblocking, returns true if successful. 235 | func (c *MConnection) TrySend(chID byte, msg interface{}) bool { 236 | if !c.IsRunning() { 237 | return false 238 | } 239 | 240 | log.Debug("TrySend", "channel", chID, "conn", c, "msg", msg) 241 | 242 | // Send message to channel. 243 | channel, ok := c.channelsIdx[chID] 244 | if !ok { 245 | log.Error(cmn.Fmt("Cannot send bytes, unknown channel %X", chID)) 246 | return false 247 | } 248 | 249 | ok = channel.trySendBytes(wire.BinaryBytes(msg)) 250 | if ok { 251 | // Wake up sendRoutine if necessary 252 | select { 253 | case c.send <- struct{}{}: 254 | default: 255 | } 256 | } 257 | 258 | return ok 259 | } 260 | 261 | // CanSend returns true if you can send more data onto the chID, false 262 | // otherwise. Use only as a heuristic. 263 | func (c *MConnection) CanSend(chID byte) bool { 264 | if !c.IsRunning() { 265 | return false 266 | } 267 | 268 | channel, ok := c.channelsIdx[chID] 269 | if !ok { 270 | log.Error(cmn.Fmt("Unknown channel %X", chID)) 271 | return false 272 | } 273 | return channel.canSend() 274 | } 275 | 276 | // sendRoutine polls for packets to send from channels. 277 | func (c *MConnection) sendRoutine() { 278 | defer c._recover() 279 | 280 | FOR_LOOP: 281 | for { 282 | var n int 283 | var err error 284 | select { 285 | case <-c.flushTimer.Ch: 286 | // NOTE: flushTimer.Set() must be called every time 287 | // something is written to .bufWriter. 288 | c.flush() 289 | case <-c.chStatsTimer.Ch: 290 | for _, channel := range c.channels { 291 | channel.updateStats() 292 | } 293 | case <-c.pingTimer.Ch: 294 | log.Debug("Send Ping") 295 | wire.WriteByte(packetTypePing, c.bufWriter, &n, &err) 296 | c.sendMonitor.Update(int(n)) 297 | c.flush() 298 | case <-c.pong: 299 | log.Debug("Send Pong") 300 | wire.WriteByte(packetTypePong, c.bufWriter, &n, &err) 301 | c.sendMonitor.Update(int(n)) 302 | c.flush() 303 | case <-c.quit: 304 | break FOR_LOOP 305 | case <-c.send: 306 | // Send some msgPackets 307 | eof := c.sendSomeMsgPackets() 308 | if !eof { 309 | // Keep sendRoutine awake. 310 | select { 311 | case c.send <- struct{}{}: 312 | default: 313 | } 314 | } 315 | } 316 | 317 | if !c.IsRunning() { 318 | break FOR_LOOP 319 | } 320 | if err != nil { 321 | log.Warn("Connection failed @ sendRoutine", "conn", c, "error", err) 322 | c.stopForError(err) 323 | break FOR_LOOP 324 | } 325 | } 326 | 327 | // Cleanup 328 | } 329 | 330 | // Returns true if messages from channels were exhausted. 331 | // Blocks in accordance to .sendMonitor throttling. 332 | func (c *MConnection) sendSomeMsgPackets() bool { 333 | // Block until .sendMonitor says we can write. 334 | // Once we're ready we send more than we asked for, 335 | // but amortized it should even out. 336 | c.sendMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.SendRate), true) 337 | 338 | // Now send some msgPackets. 339 | for i := 0; i < numBatchMsgPackets; i++ { 340 | if c.sendMsgPacket() { 341 | return true 342 | } 343 | } 344 | return false 345 | } 346 | 347 | // Returns true if messages from channels were exhausted. 348 | func (c *MConnection) sendMsgPacket() bool { 349 | // Choose a channel to create a msgPacket from. 350 | // The chosen channel will be the one whose recentlySent/priority is the least. 351 | var leastRatio float32 = math.MaxFloat32 352 | var leastChannel *Channel 353 | for _, channel := range c.channels { 354 | // If nothing to send, skip this channel 355 | if !channel.isSendPending() { 356 | continue 357 | } 358 | // Get ratio, and keep track of lowest ratio. 359 | ratio := float32(channel.recentlySent) / float32(channel.priority) 360 | if ratio < leastRatio { 361 | leastRatio = ratio 362 | leastChannel = channel 363 | } 364 | } 365 | 366 | // Nothing to send? 367 | if leastChannel == nil { 368 | return true 369 | } else { 370 | // log.Info("Found a msgPacket to send") 371 | } 372 | 373 | // Make & send a msgPacket from this channel 374 | n, err := leastChannel.writeMsgPacketTo(c.bufWriter) 375 | if err != nil { 376 | log.Warn("Failed to write msgPacket", "error", err) 377 | c.stopForError(err) 378 | return true 379 | } 380 | c.sendMonitor.Update(int(n)) 381 | c.flushTimer.Set() 382 | return false 383 | } 384 | 385 | // recvRoutine reads msgPackets and reconstructs the message using the channels' "recving" buffer. 386 | // After a whole message has been assembled, it's pushed to onReceive(). 387 | // Blocks depending on how the connection is throttled. 388 | func (c *MConnection) recvRoutine() { 389 | defer c._recover() 390 | 391 | FOR_LOOP: 392 | for { 393 | // Block until .recvMonitor says we can read. 394 | c.recvMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.RecvRate), true) 395 | 396 | /* 397 | // Peek into bufReader for debugging 398 | if numBytes := c.bufReader.Buffered(); numBytes > 0 { 399 | log.Info("Peek connection buffer", "numBytes", numBytes, "bytes", log15.Lazy{func() []byte { 400 | bytes, err := c.bufReader.Peek(MinInt(numBytes, 100)) 401 | if err == nil { 402 | return bytes 403 | } else { 404 | log.Warn("Error peeking connection buffer", "error", err) 405 | return nil 406 | } 407 | }}) 408 | } 409 | */ 410 | 411 | // Read packet type 412 | var n int 413 | var err error 414 | pktType := wire.ReadByte(c.bufReader, &n, &err) 415 | c.recvMonitor.Update(int(n)) 416 | if err != nil { 417 | if c.IsRunning() { 418 | log.Warn("Connection failed @ recvRoutine (reading byte)", "conn", c, "error", err) 419 | c.stopForError(err) 420 | } 421 | break FOR_LOOP 422 | } 423 | 424 | // Read more depending on packet type. 425 | switch pktType { 426 | case packetTypePing: 427 | // TODO: prevent abuse, as they cause flush()'s. 428 | log.Debug("Receive Ping") 429 | c.pong <- struct{}{} 430 | case packetTypePong: 431 | // do nothing 432 | log.Debug("Receive Pong") 433 | case packetTypeMsg: 434 | pkt, n, err := msgPacket{}, int(0), error(nil) 435 | wire.ReadBinaryPtr(&pkt, c.bufReader, maxMsgPacketTotalSize, &n, &err) 436 | c.recvMonitor.Update(int(n)) 437 | if err != nil { 438 | if c.IsRunning() { 439 | log.Warn("Connection failed @ recvRoutine", "conn", c, "error", err) 440 | c.stopForError(err) 441 | } 442 | break FOR_LOOP 443 | } 444 | channel, ok := c.channelsIdx[pkt.ChannelID] 445 | if !ok || channel == nil { 446 | cmn.PanicQ(cmn.Fmt("Unknown channel %X", pkt.ChannelID)) 447 | } 448 | msgBytes, err := channel.recvMsgPacket(pkt) 449 | if err != nil { 450 | if c.IsRunning() { 451 | log.Warn("Connection failed @ recvRoutine", "conn", c, "error", err) 452 | c.stopForError(err) 453 | } 454 | break FOR_LOOP 455 | } 456 | if msgBytes != nil { 457 | log.Debug("Received bytes", "chID", pkt.ChannelID, "msgBytes", msgBytes) 458 | c.onReceive(pkt.ChannelID, msgBytes) 459 | } 460 | default: 461 | cmn.PanicSanity(cmn.Fmt("Unknown message type %X", pktType)) 462 | } 463 | 464 | // TODO: shouldn't this go in the sendRoutine? 465 | // Better to send a ping packet when *we* haven't sent anything for a while. 466 | c.pingTimer.Reset() 467 | } 468 | 469 | // Cleanup 470 | close(c.pong) 471 | for _ = range c.pong { 472 | // Drain 473 | } 474 | } 475 | 476 | type ConnectionStatus struct { 477 | SendMonitor flow.Status 478 | RecvMonitor flow.Status 479 | Channels []ChannelStatus 480 | } 481 | 482 | type ChannelStatus struct { 483 | ID byte 484 | SendQueueCapacity int 485 | SendQueueSize int 486 | Priority int 487 | RecentlySent int64 488 | } 489 | 490 | func (c *MConnection) Status() ConnectionStatus { 491 | var status ConnectionStatus 492 | status.SendMonitor = c.sendMonitor.Status() 493 | status.RecvMonitor = c.recvMonitor.Status() 494 | status.Channels = make([]ChannelStatus, len(c.channels)) 495 | for i, channel := range c.channels { 496 | status.Channels[i] = ChannelStatus{ 497 | ID: channel.id, 498 | SendQueueCapacity: cap(channel.sendQueue), 499 | SendQueueSize: int(channel.sendQueueSize), // TODO use atomic 500 | Priority: channel.priority, 501 | RecentlySent: channel.recentlySent, 502 | } 503 | } 504 | return status 505 | } 506 | 507 | //----------------------------------------------------------------------------- 508 | 509 | type ChannelDescriptor struct { 510 | ID byte 511 | Priority int 512 | SendQueueCapacity int 513 | RecvBufferCapacity int 514 | RecvMessageCapacity int 515 | } 516 | 517 | func (chDesc *ChannelDescriptor) FillDefaults() { 518 | if chDesc.SendQueueCapacity == 0 { 519 | chDesc.SendQueueCapacity = defaultSendQueueCapacity 520 | } 521 | if chDesc.RecvBufferCapacity == 0 { 522 | chDesc.RecvBufferCapacity = defaultRecvBufferCapacity 523 | } 524 | if chDesc.RecvMessageCapacity == 0 { 525 | chDesc.RecvMessageCapacity = defaultRecvMessageCapacity 526 | } 527 | } 528 | 529 | // TODO: lowercase. 530 | // NOTE: not goroutine-safe. 531 | type Channel struct { 532 | conn *MConnection 533 | desc *ChannelDescriptor 534 | id byte 535 | sendQueue chan []byte 536 | sendQueueSize int32 // atomic. 537 | recving []byte 538 | sending []byte 539 | priority int 540 | recentlySent int64 // exponential moving average 541 | } 542 | 543 | func newChannel(conn *MConnection, desc *ChannelDescriptor) *Channel { 544 | desc.FillDefaults() 545 | if desc.Priority <= 0 { 546 | cmn.PanicSanity("Channel default priority must be a postive integer") 547 | } 548 | return &Channel{ 549 | conn: conn, 550 | desc: desc, 551 | id: desc.ID, 552 | sendQueue: make(chan []byte, desc.SendQueueCapacity), 553 | recving: make([]byte, 0, desc.RecvBufferCapacity), 554 | priority: desc.Priority, 555 | } 556 | } 557 | 558 | // Queues message to send to this channel. 559 | // Goroutine-safe 560 | // Times out (and returns false) after defaultSendTimeout 561 | func (ch *Channel) sendBytes(bytes []byte) bool { 562 | select { 563 | case ch.sendQueue <- bytes: 564 | atomic.AddInt32(&ch.sendQueueSize, 1) 565 | return true 566 | case <-time.After(defaultSendTimeout): 567 | return false 568 | } 569 | } 570 | 571 | // Queues message to send to this channel. 572 | // Nonblocking, returns true if successful. 573 | // Goroutine-safe 574 | func (ch *Channel) trySendBytes(bytes []byte) bool { 575 | select { 576 | case ch.sendQueue <- bytes: 577 | atomic.AddInt32(&ch.sendQueueSize, 1) 578 | return true 579 | default: 580 | return false 581 | } 582 | } 583 | 584 | // Goroutine-safe 585 | func (ch *Channel) loadSendQueueSize() (size int) { 586 | return int(atomic.LoadInt32(&ch.sendQueueSize)) 587 | } 588 | 589 | // Goroutine-safe 590 | // Use only as a heuristic. 591 | func (ch *Channel) canSend() bool { 592 | return ch.loadSendQueueSize() < defaultSendQueueCapacity 593 | } 594 | 595 | // Returns true if any msgPackets are pending to be sent. 596 | // Call before calling nextMsgPacket() 597 | // Goroutine-safe 598 | func (ch *Channel) isSendPending() bool { 599 | if len(ch.sending) == 0 { 600 | if len(ch.sendQueue) == 0 { 601 | return false 602 | } 603 | ch.sending = <-ch.sendQueue 604 | } 605 | return true 606 | } 607 | 608 | // Creates a new msgPacket to send. 609 | // Not goroutine-safe 610 | func (ch *Channel) nextMsgPacket() msgPacket { 611 | packet := msgPacket{} 612 | packet.ChannelID = byte(ch.id) 613 | packet.Bytes = ch.sending[:cmn.MinInt(maxMsgPacketPayloadSize, len(ch.sending))] 614 | if len(ch.sending) <= maxMsgPacketPayloadSize { 615 | packet.EOF = byte(0x01) 616 | ch.sending = nil 617 | atomic.AddInt32(&ch.sendQueueSize, -1) // decrement sendQueueSize 618 | } else { 619 | packet.EOF = byte(0x00) 620 | ch.sending = ch.sending[cmn.MinInt(maxMsgPacketPayloadSize, len(ch.sending)):] 621 | } 622 | return packet 623 | } 624 | 625 | // Writes next msgPacket to w. 626 | // Not goroutine-safe 627 | func (ch *Channel) writeMsgPacketTo(w io.Writer) (n int, err error) { 628 | packet := ch.nextMsgPacket() 629 | log.Debug("Write Msg Packet", "conn", ch.conn, "packet", packet) 630 | wire.WriteByte(packetTypeMsg, w, &n, &err) 631 | wire.WriteBinary(packet, w, &n, &err) 632 | if err == nil { 633 | ch.recentlySent += int64(n) 634 | } 635 | return 636 | } 637 | 638 | // Handles incoming msgPackets. Returns a msg bytes if msg is complete. 639 | // Not goroutine-safe 640 | func (ch *Channel) recvMsgPacket(packet msgPacket) ([]byte, error) { 641 | // log.Debug("Read Msg Packet", "conn", ch.conn, "packet", packet) 642 | if ch.desc.RecvMessageCapacity < len(ch.recving)+len(packet.Bytes) { 643 | return nil, wire.ErrBinaryReadOverflow 644 | } 645 | ch.recving = append(ch.recving, packet.Bytes...) 646 | if packet.EOF == byte(0x01) { 647 | msgBytes := ch.recving 648 | // clear the slice without re-allocating. 649 | // http://stackoverflow.com/questions/16971741/how-do-you-clear-a-slice-in-go 650 | // suggests this could be a memory leak, but we might as well keep the memory for the channel until it closes, 651 | // at which point the recving slice stops being used and should be garbage collected 652 | ch.recving = ch.recving[:0] // make([]byte, 0, ch.desc.RecvBufferCapacity) 653 | return msgBytes, nil 654 | } 655 | return nil, nil 656 | } 657 | 658 | // Call this periodically to update stats for throttling purposes. 659 | // Not goroutine-safe 660 | func (ch *Channel) updateStats() { 661 | // Exponential decay of stats. 662 | // TODO: optimize. 663 | ch.recentlySent = int64(float64(ch.recentlySent) * 0.8) 664 | } 665 | 666 | //----------------------------------------------------------------------------- 667 | 668 | const ( 669 | maxMsgPacketPayloadSize = 1024 670 | maxMsgPacketOverheadSize = 10 // It's actually lower but good enough 671 | maxMsgPacketTotalSize = maxMsgPacketPayloadSize + maxMsgPacketOverheadSize 672 | packetTypePing = byte(0x01) 673 | packetTypePong = byte(0x02) 674 | packetTypeMsg = byte(0x03) 675 | ) 676 | 677 | // Messages in channels are chopped into smaller msgPackets for multiplexing. 678 | type msgPacket struct { 679 | ChannelID byte 680 | EOF byte // 1 means message ends here. 681 | Bytes []byte 682 | } 683 | 684 | func (p msgPacket) String() string { 685 | return fmt.Sprintf("MsgPacket{%X:%X T:%X}", p.ChannelID, p.Bytes, p.EOF) 686 | } 687 | -------------------------------------------------------------------------------- /connection_test.go: -------------------------------------------------------------------------------- 1 | package p2p_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | p2p "github.com/tendermint/go-p2p" 11 | ) 12 | 13 | func createMConnection(conn net.Conn) *p2p.MConnection { 14 | onReceive := func(chID byte, msgBytes []byte) { 15 | } 16 | onError := func(r interface{}) { 17 | } 18 | return createMConnectionWithCallbacks(conn, onReceive, onError) 19 | } 20 | 21 | func createMConnectionWithCallbacks(conn net.Conn, onReceive func(chID byte, msgBytes []byte), onError func(r interface{})) *p2p.MConnection { 22 | chDescs := []*p2p.ChannelDescriptor{&p2p.ChannelDescriptor{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} 23 | return p2p.NewMConnection(conn, chDescs, onReceive, onError) 24 | } 25 | 26 | func TestMConnectionSend(t *testing.T) { 27 | assert, require := assert.New(t), require.New(t) 28 | 29 | server, client := net.Pipe() 30 | defer server.Close() 31 | defer client.Close() 32 | 33 | mconn := createMConnection(client) 34 | _, err := mconn.Start() 35 | require.Nil(err) 36 | defer mconn.Stop() 37 | 38 | msg := "Ant-Man" 39 | assert.True(mconn.Send(0x01, msg)) 40 | // Note: subsequent Send/TrySend calls could pass because we are reading from 41 | // the send queue in a separate goroutine. 42 | server.Read(make([]byte, len(msg))) 43 | assert.True(mconn.CanSend(0x01)) 44 | 45 | msg = "Spider-Man" 46 | assert.True(mconn.TrySend(0x01, msg)) 47 | server.Read(make([]byte, len(msg))) 48 | 49 | assert.False(mconn.CanSend(0x05), "CanSend should return false because channel is unknown") 50 | assert.False(mconn.Send(0x05, "Absorbing Man"), "Send should return false because channel is unknown") 51 | } 52 | 53 | func TestMConnectionReceive(t *testing.T) { 54 | assert, require := assert.New(t), require.New(t) 55 | 56 | server, client := net.Pipe() 57 | defer server.Close() 58 | defer client.Close() 59 | 60 | receivedCh := make(chan []byte) 61 | errorsCh := make(chan interface{}) 62 | onReceive := func(chID byte, msgBytes []byte) { 63 | receivedCh <- msgBytes 64 | } 65 | onError := func(r interface{}) { 66 | errorsCh <- r 67 | } 68 | mconn1 := createMConnectionWithCallbacks(client, onReceive, onError) 69 | _, err := mconn1.Start() 70 | require.Nil(err) 71 | defer mconn1.Stop() 72 | 73 | mconn2 := createMConnection(server) 74 | _, err = mconn2.Start() 75 | require.Nil(err) 76 | defer mconn2.Stop() 77 | 78 | msg := "Cyclops" 79 | assert.True(mconn2.Send(0x01, msg)) 80 | 81 | select { 82 | case receivedBytes := <-receivedCh: 83 | assert.Equal([]byte(msg), receivedBytes[2:]) // first 3 bytes are internal 84 | case err := <-errorsCh: 85 | t.Fatalf("Expected %s, got %+v", msg, err) 86 | case <-time.After(500 * time.Millisecond): 87 | t.Fatalf("Did not receive %s message in 500ms", msg) 88 | } 89 | } 90 | 91 | func TestMConnectionStatus(t *testing.T) { 92 | assert, require := assert.New(t), require.New(t) 93 | 94 | server, client := net.Pipe() 95 | defer server.Close() 96 | defer client.Close() 97 | 98 | mconn := createMConnection(client) 99 | _, err := mconn.Start() 100 | require.Nil(err) 101 | defer mconn.Stop() 102 | 103 | status := mconn.Status() 104 | assert.NotNil(status) 105 | assert.Zero(status.Channels[0].SendQueueSize) 106 | } 107 | 108 | func TestMConnectionStopsAndReturnsError(t *testing.T) { 109 | assert, require := assert.New(t), require.New(t) 110 | 111 | server, client := net.Pipe() 112 | defer server.Close() 113 | defer client.Close() 114 | 115 | receivedCh := make(chan []byte) 116 | errorsCh := make(chan interface{}) 117 | onReceive := func(chID byte, msgBytes []byte) { 118 | receivedCh <- msgBytes 119 | } 120 | onError := func(r interface{}) { 121 | errorsCh <- r 122 | } 123 | mconn := createMConnectionWithCallbacks(client, onReceive, onError) 124 | _, err := mconn.Start() 125 | require.Nil(err) 126 | defer mconn.Stop() 127 | 128 | client.Close() 129 | 130 | select { 131 | case receivedBytes := <-receivedCh: 132 | t.Fatalf("Expected error, got %v", receivedBytes) 133 | case err := <-errorsCh: 134 | assert.NotNil(err) 135 | assert.False(mconn.IsRunning()) 136 | case <-time.After(500 * time.Millisecond): 137 | t.Fatal("Did not receive error in 500ms") 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /fuzz.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "math/rand" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const ( 11 | // FuzzModeDrop is a mode in which we randomly drop reads/writes, connections or sleep 12 | FuzzModeDrop = iota 13 | // FuzzModeDelay is a mode in which we randomly sleep 14 | FuzzModeDelay 15 | ) 16 | 17 | // FuzzedConnection wraps any net.Conn and depending on the mode either delays 18 | // reads/writes or randomly drops reads/writes/connections. 19 | type FuzzedConnection struct { 20 | conn net.Conn 21 | 22 | mtx sync.Mutex 23 | start <-chan time.Time 24 | active bool 25 | 26 | config *FuzzConnConfig 27 | } 28 | 29 | // FuzzConnConfig is a FuzzedConnection configuration. 30 | type FuzzConnConfig struct { 31 | Mode int 32 | MaxDelay time.Duration 33 | ProbDropRW float64 34 | ProbDropConn float64 35 | ProbSleep float64 36 | } 37 | 38 | // DefaultFuzzConnConfig returns the default config. 39 | func DefaultFuzzConnConfig() *FuzzConnConfig { 40 | return &FuzzConnConfig{ 41 | Mode: FuzzModeDrop, 42 | MaxDelay: 3 * time.Second, 43 | ProbDropRW: 0.2, 44 | ProbDropConn: 0.00, 45 | ProbSleep: 0.00, 46 | } 47 | } 48 | 49 | // FuzzConn creates a new FuzzedConnection. Fuzzing starts immediately. 50 | func FuzzConn(conn net.Conn) net.Conn { 51 | return FuzzConnFromConfig(conn, DefaultFuzzConnConfig()) 52 | } 53 | 54 | // FuzzConnFromConfig creates a new FuzzedConnection from a config. Fuzzing 55 | // starts immediately. 56 | func FuzzConnFromConfig(conn net.Conn, config *FuzzConnConfig) net.Conn { 57 | return &FuzzedConnection{ 58 | conn: conn, 59 | start: make(<-chan time.Time), 60 | active: true, 61 | config: config, 62 | } 63 | } 64 | 65 | // FuzzConnAfter creates a new FuzzedConnection. Fuzzing starts when the 66 | // duration elapses. 67 | func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn { 68 | return FuzzConnAfterFromConfig(conn, d, DefaultFuzzConnConfig()) 69 | } 70 | 71 | // FuzzConnAfterFromConfig creates a new FuzzedConnection from a config. 72 | // Fuzzing starts when the duration elapses. 73 | func FuzzConnAfterFromConfig(conn net.Conn, d time.Duration, config *FuzzConnConfig) net.Conn { 74 | return &FuzzedConnection{ 75 | conn: conn, 76 | start: time.After(d), 77 | active: false, 78 | config: config, 79 | } 80 | } 81 | 82 | // Config returns the connection's config. 83 | func (fc *FuzzedConnection) Config() *FuzzConnConfig { 84 | return fc.config 85 | } 86 | 87 | // Read implements net.Conn. 88 | func (fc *FuzzedConnection) Read(data []byte) (n int, err error) { 89 | if fc.fuzz() { 90 | return 0, nil 91 | } 92 | return fc.conn.Read(data) 93 | } 94 | 95 | // Write implements net.Conn. 96 | func (fc *FuzzedConnection) Write(data []byte) (n int, err error) { 97 | if fc.fuzz() { 98 | return 0, nil 99 | } 100 | return fc.conn.Write(data) 101 | } 102 | 103 | // Close implements net.Conn. 104 | func (fc *FuzzedConnection) Close() error { return fc.conn.Close() } 105 | 106 | // LocalAddr implements net.Conn. 107 | func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() } 108 | 109 | // RemoteAddr implements net.Conn. 110 | func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() } 111 | 112 | // SetDeadline implements net.Conn. 113 | func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) } 114 | 115 | // SetReadDeadline implements net.Conn. 116 | func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error { 117 | return fc.conn.SetReadDeadline(t) 118 | } 119 | 120 | // SetWriteDeadline implements net.Conn. 121 | func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error { 122 | return fc.conn.SetWriteDeadline(t) 123 | } 124 | 125 | func (fc *FuzzedConnection) randomDuration() time.Duration { 126 | maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000) 127 | return time.Millisecond * time.Duration(rand.Int()%maxDelayMillis) 128 | } 129 | 130 | // implements the fuzz (delay, kill conn) 131 | // and returns whether or not the read/write should be ignored 132 | func (fc *FuzzedConnection) fuzz() bool { 133 | if !fc.shouldFuzz() { 134 | return false 135 | } 136 | 137 | switch fc.config.Mode { 138 | case FuzzModeDrop: 139 | // randomly drop the r/w, drop the conn, or sleep 140 | r := rand.Float64() 141 | if r <= fc.config.ProbDropRW { 142 | return true 143 | } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn { 144 | // XXX: can't this fail because machine precision? 145 | // XXX: do we need an error? 146 | fc.Close() 147 | return true 148 | } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn+fc.config.ProbSleep { 149 | time.Sleep(fc.randomDuration()) 150 | } 151 | case FuzzModeDelay: 152 | // sleep a bit 153 | time.Sleep(fc.randomDuration()) 154 | } 155 | return false 156 | } 157 | 158 | func (fc *FuzzedConnection) shouldFuzz() bool { 159 | if fc.active { 160 | return true 161 | } 162 | 163 | fc.mtx.Lock() 164 | defer fc.mtx.Unlock() 165 | 166 | select { 167 | case <-fc.start: 168 | fc.active = true 169 | return true 170 | default: 171 | return false 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: f3d76bef9548cc37ad6038cb55f0812bac7e64735a99995c9da85010eef27f50 2 | updated: 2017-04-19T00:00:50.949249104-04:00 3 | imports: 4 | - name: github.com/btcsuite/btcd 5 | version: b8df516b4b267acf2de46be593a9d948d1d2c420 6 | subpackages: 7 | - btcec 8 | - name: github.com/btcsuite/fastsha256 9 | version: 637e656429416087660c84436a2a035d69d54e2e 10 | - name: github.com/BurntSushi/toml 11 | version: 99064174e013895bbd9b025c31100bd1d9b590ca 12 | - name: github.com/go-stack/stack 13 | version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 14 | - name: github.com/mattn/go-colorable 15 | version: 9fdad7c47650b7d2e1da50644c1f4ba7f172f252 16 | - name: github.com/mattn/go-isatty 17 | version: 56b76bdf51f7708750eac80fa38b952bb9f32639 18 | - name: github.com/pkg/errors 19 | version: 645ef00459ed84a119197bfb8d8205042c6df63d 20 | - name: github.com/tendermint/ed25519 21 | version: 1f52c6f8b8a5c7908aff4497c186af344b428925 22 | subpackages: 23 | - edwards25519 24 | - extra25519 25 | - name: github.com/tendermint/go-common 26 | version: f9e3db037330c8a8d61d3966de8473eaf01154fa 27 | - name: github.com/tendermint/go-config 28 | version: 620dcbbd7d587cf3599dedbf329b64311b0c307a 29 | - name: github.com/tendermint/go-crypto 30 | version: 0ca2c6fdb0706001ca4c4b9b80c9f428e8cf39da 31 | - name: github.com/tendermint/go-data 32 | version: e7fcc6d081ec8518912fcdc103188275f83a3ee5 33 | - name: github.com/tendermint/go-flowrate 34 | version: a20c98e61957faa93b4014fbd902f20ab9317a6a 35 | subpackages: 36 | - flowrate 37 | - name: github.com/tendermint/go-logger 38 | version: cefb3a45c0bf3c493a04e9bcd9b1540528be59f2 39 | - name: github.com/tendermint/go-wire 40 | version: c1c9a57ab8038448ddea1714c0698f8051e5748c 41 | - name: github.com/tendermint/log15 42 | version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 43 | subpackages: 44 | - term 45 | - name: golang.org/x/crypto 46 | version: 1f22c0103821b9390939b6776727195525381532 47 | subpackages: 48 | - curve25519 49 | - nacl/box 50 | - nacl/secretbox 51 | - openpgp/armor 52 | - openpgp/errors 53 | - poly1305 54 | - ripemd160 55 | - salsa20/salsa 56 | - name: golang.org/x/sys 57 | version: 50c6bc5e4292a1d4e65c6e9be5f53be28bcbe28e 58 | subpackages: 59 | - unix 60 | testImports: 61 | - name: github.com/davecgh/go-spew 62 | version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 63 | subpackages: 64 | - spew 65 | - name: github.com/pmezard/go-difflib 66 | version: d8ed2627bdf02c080bf22230dbb337003b7aba2d 67 | subpackages: 68 | - difflib 69 | - name: github.com/stretchr/testify 70 | version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 71 | subpackages: 72 | - assert 73 | - require 74 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/tendermint/go-p2p 2 | import: 3 | - package: github.com/tendermint/go-common 4 | version: develop 5 | - package: github.com/tendermint/go-config 6 | version: develop 7 | - package: github.com/tendermint/go-crypto 8 | version: develop 9 | - package: github.com/tendermint/go-data 10 | version: develop 11 | - package: github.com/tendermint/go-flowrate 12 | subpackages: 13 | - flowrate 14 | - package: github.com/tendermint/go-logger 15 | version: develop 16 | - package: github.com/tendermint/go-wire 17 | version: develop 18 | - package: github.com/tendermint/log15 19 | - package: golang.org/x/crypto 20 | subpackages: 21 | - nacl/box 22 | - nacl/secretbox 23 | - ripemd160 24 | - package: github.com/pkg/errors 25 | testImport: 26 | - package: github.com/stretchr/testify 27 | subpackages: 28 | - assert 29 | - require 30 | -------------------------------------------------------------------------------- /ip_range_counter.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // TODO Test 8 | func AddToIPRangeCounts(counts map[string]int, ip string) map[string]int { 9 | changes := make(map[string]int) 10 | ipParts := strings.Split(ip, ":") 11 | for i := 1; i < len(ipParts); i++ { 12 | prefix := strings.Join(ipParts[:i], ":") 13 | counts[prefix] += 1 14 | changes[prefix] = counts[prefix] 15 | } 16 | return changes 17 | } 18 | 19 | // TODO Test 20 | func CheckIPRangeCounts(counts map[string]int, limits []int) bool { 21 | for prefix, count := range counts { 22 | ipParts := strings.Split(prefix, ":") 23 | numParts := len(ipParts) 24 | if limits[numParts] < count { 25 | return false 26 | } 27 | } 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /listener.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | "time" 8 | 9 | . "github.com/tendermint/go-common" 10 | "github.com/tendermint/go-p2p/upnp" 11 | ) 12 | 13 | type Listener interface { 14 | Connections() <-chan net.Conn 15 | InternalAddress() *NetAddress 16 | ExternalAddress() *NetAddress 17 | String() string 18 | Stop() bool 19 | } 20 | 21 | // Implements Listener 22 | type DefaultListener struct { 23 | BaseService 24 | 25 | listener net.Listener 26 | intAddr *NetAddress 27 | extAddr *NetAddress 28 | connections chan net.Conn 29 | } 30 | 31 | const ( 32 | numBufferedConnections = 10 33 | defaultExternalPort = 8770 34 | tryListenSeconds = 5 35 | ) 36 | 37 | func splitHostPort(addr string) (host string, port int) { 38 | host, portStr, err := net.SplitHostPort(addr) 39 | if err != nil { 40 | PanicSanity(err) 41 | } 42 | port, err = strconv.Atoi(portStr) 43 | if err != nil { 44 | PanicSanity(err) 45 | } 46 | return host, port 47 | } 48 | 49 | // skipUPNP: If true, does not try getUPNPExternalAddress() 50 | func NewDefaultListener(protocol string, lAddr string, skipUPNP bool) Listener { 51 | // Local listen IP & port 52 | lAddrIP, lAddrPort := splitHostPort(lAddr) 53 | 54 | // Create listener 55 | var listener net.Listener 56 | var err error 57 | for i := 0; i < tryListenSeconds; i++ { 58 | listener, err = net.Listen(protocol, lAddr) 59 | if err == nil { 60 | break 61 | } else if i < tryListenSeconds-1 { 62 | time.Sleep(time.Second * 1) 63 | } 64 | } 65 | if err != nil { 66 | PanicCrisis(err) 67 | } 68 | // Actual listener local IP & port 69 | listenerIP, listenerPort := splitHostPort(listener.Addr().String()) 70 | log.Info("Local listener", "ip", listenerIP, "port", listenerPort) 71 | 72 | // Determine internal address... 73 | var intAddr *NetAddress 74 | intAddr, err = NewNetAddressString(lAddr) 75 | if err != nil { 76 | PanicCrisis(err) 77 | } 78 | 79 | // Determine external address... 80 | var extAddr *NetAddress 81 | if !skipUPNP { 82 | // If the lAddrIP is INADDR_ANY, try UPnP 83 | if lAddrIP == "" || lAddrIP == "0.0.0.0" { 84 | extAddr = getUPNPExternalAddress(lAddrPort, listenerPort) 85 | } 86 | } 87 | // Otherwise just use the local address... 88 | if extAddr == nil { 89 | extAddr = getNaiveExternalAddress(listenerPort) 90 | } 91 | if extAddr == nil { 92 | PanicCrisis("Could not determine external address!") 93 | } 94 | 95 | dl := &DefaultListener{ 96 | listener: listener, 97 | intAddr: intAddr, 98 | extAddr: extAddr, 99 | connections: make(chan net.Conn, numBufferedConnections), 100 | } 101 | dl.BaseService = *NewBaseService(log, "DefaultListener", dl) 102 | dl.Start() // Started upon construction 103 | return dl 104 | } 105 | 106 | func (l *DefaultListener) OnStart() error { 107 | l.BaseService.OnStart() 108 | go l.listenRoutine() 109 | return nil 110 | } 111 | 112 | func (l *DefaultListener) OnStop() { 113 | l.BaseService.OnStop() 114 | l.listener.Close() 115 | } 116 | 117 | // Accept connections and pass on the channel 118 | func (l *DefaultListener) listenRoutine() { 119 | for { 120 | conn, err := l.listener.Accept() 121 | 122 | if !l.IsRunning() { 123 | break // Go to cleanup 124 | } 125 | 126 | // listener wasn't stopped, 127 | // yet we encountered an error. 128 | if err != nil { 129 | PanicCrisis(err) 130 | } 131 | 132 | l.connections <- conn 133 | } 134 | 135 | // Cleanup 136 | close(l.connections) 137 | for _ = range l.connections { 138 | // Drain 139 | } 140 | } 141 | 142 | // A channel of inbound connections. 143 | // It gets closed when the listener closes. 144 | func (l *DefaultListener) Connections() <-chan net.Conn { 145 | return l.connections 146 | } 147 | 148 | func (l *DefaultListener) InternalAddress() *NetAddress { 149 | return l.intAddr 150 | } 151 | 152 | func (l *DefaultListener) ExternalAddress() *NetAddress { 153 | return l.extAddr 154 | } 155 | 156 | // NOTE: The returned listener is already Accept()'ing. 157 | // So it's not suitable to pass into http.Serve(). 158 | func (l *DefaultListener) NetListener() net.Listener { 159 | return l.listener 160 | } 161 | 162 | func (l *DefaultListener) String() string { 163 | return fmt.Sprintf("Listener(@%v)", l.extAddr) 164 | } 165 | 166 | /* external address helpers */ 167 | 168 | // UPNP external address discovery & port mapping 169 | func getUPNPExternalAddress(externalPort, internalPort int) *NetAddress { 170 | log.Info("Getting UPNP external address") 171 | nat, err := upnp.Discover() 172 | if err != nil { 173 | log.Info("Could not perform UPNP discover", "error", err) 174 | return nil 175 | } 176 | 177 | ext, err := nat.GetExternalAddress() 178 | if err != nil { 179 | log.Info("Could not get UPNP external address", "error", err) 180 | return nil 181 | } 182 | 183 | // UPnP can't seem to get the external port, so let's just be explicit. 184 | if externalPort == 0 { 185 | externalPort = defaultExternalPort 186 | } 187 | 188 | externalPort, err = nat.AddPortMapping("tcp", externalPort, internalPort, "tendermint", 0) 189 | if err != nil { 190 | log.Info("Could not add UPNP port mapping", "error", err) 191 | return nil 192 | } 193 | 194 | log.Info("Got UPNP external address", "address", ext) 195 | return NewNetAddressIPPort(ext, uint16(externalPort)) 196 | } 197 | 198 | // TODO: use syscalls: http://pastebin.com/9exZG4rh 199 | func getNaiveExternalAddress(port int) *NetAddress { 200 | addrs, err := net.InterfaceAddrs() 201 | if err != nil { 202 | PanicCrisis(Fmt("Could not fetch interface addresses: %v", err)) 203 | } 204 | 205 | for _, a := range addrs { 206 | ipnet, ok := a.(*net.IPNet) 207 | if !ok { 208 | continue 209 | } 210 | v4 := ipnet.IP.To4() 211 | if v4 == nil || v4[0] == 127 { 212 | continue 213 | } // loopback 214 | return NewNetAddressIPPort(ipnet.IP, uint16(port)) 215 | } 216 | return nil 217 | } 218 | -------------------------------------------------------------------------------- /listener_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestListener(t *testing.T) { 9 | // Create a listener 10 | l := NewDefaultListener("tcp", ":8001", true) 11 | 12 | // Dial the listener 13 | lAddr := l.ExternalAddress() 14 | connOut, err := lAddr.Dial() 15 | if err != nil { 16 | t.Fatalf("Could not connect to listener address %v", lAddr) 17 | } else { 18 | t.Logf("Created a connection to listener address %v", lAddr) 19 | } 20 | connIn, ok := <-l.Connections() 21 | if !ok { 22 | t.Fatalf("Could not get inbound connection from listener") 23 | } 24 | 25 | msg := []byte("hi!") 26 | go connIn.Write(msg) 27 | b := make([]byte, 32) 28 | n, err := connOut.Read(b) 29 | if err != nil { 30 | t.Fatalf("Error reading off connection: %v", err) 31 | } 32 | 33 | b = b[:n] 34 | if !bytes.Equal(msg, b) { 35 | t.Fatalf("Got %s, expected %s", b, msg) 36 | } 37 | 38 | // Close the server, no longer needed. 39 | l.Stop() 40 | } 41 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "github.com/tendermint/go-logger" 5 | ) 6 | 7 | var log = logger.New("module", "p2p") 8 | -------------------------------------------------------------------------------- /netaddress.go: -------------------------------------------------------------------------------- 1 | // Modified for Tendermint 2 | // Originally Copyright (c) 2013-2014 Conformal Systems LLC. 3 | // https://github.com/conformal/btcd/blob/master/LICENSE 4 | 5 | package p2p 6 | 7 | import ( 8 | "errors" 9 | "flag" 10 | "net" 11 | "strconv" 12 | "time" 13 | 14 | cmn "github.com/tendermint/go-common" 15 | ) 16 | 17 | // NetAddress defines information about a peer on the network 18 | // including its IP address, and port. 19 | type NetAddress struct { 20 | IP net.IP 21 | Port uint16 22 | str string 23 | } 24 | 25 | // NewNetAddress returns a new NetAddress using the provided TCP 26 | // address. When testing, other net.Addr (except TCP) will result in 27 | // using 0.0.0.0:0. When normal run, other net.Addr (except TCP) will 28 | // panic. 29 | // TODO: socks proxies? 30 | func NewNetAddress(addr net.Addr) *NetAddress { 31 | tcpAddr, ok := addr.(*net.TCPAddr) 32 | if !ok { 33 | if flag.Lookup("test.v") == nil { // normal run 34 | cmn.PanicSanity(cmn.Fmt("Only TCPAddrs are supported. Got: %v", addr)) 35 | } else { // in testing 36 | return NewNetAddressIPPort(net.IP("0.0.0.0"), 0) 37 | } 38 | } 39 | ip := tcpAddr.IP 40 | port := uint16(tcpAddr.Port) 41 | return NewNetAddressIPPort(ip, port) 42 | } 43 | 44 | // NewNetAddressString returns a new NetAddress using the provided 45 | // address in the form of "IP:Port". Also resolves the host if host 46 | // is not an IP. 47 | func NewNetAddressString(addr string) (*NetAddress, error) { 48 | 49 | host, portStr, err := net.SplitHostPort(addr) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | ip := net.ParseIP(host) 55 | if ip == nil { 56 | if len(host) > 0 { 57 | ips, err := net.LookupIP(host) 58 | if err != nil { 59 | return nil, err 60 | } 61 | ip = ips[0] 62 | } 63 | } 64 | 65 | port, err := strconv.ParseUint(portStr, 10, 16) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | na := NewNetAddressIPPort(ip, uint16(port)) 71 | return na, nil 72 | } 73 | 74 | // NewNetAddressStrings returns an array of NetAddress'es build using 75 | // the provided strings. 76 | func NewNetAddressStrings(addrs []string) ([]*NetAddress, error) { 77 | netAddrs := make([]*NetAddress, len(addrs)) 78 | for i, addr := range addrs { 79 | netAddr, err := NewNetAddressString(addr) 80 | if err != nil { 81 | return nil, errors.New(cmn.Fmt("Error in address %s: %v", addr, err)) 82 | } 83 | netAddrs[i] = netAddr 84 | } 85 | return netAddrs, nil 86 | } 87 | 88 | // NewNetAddressIPPort returns a new NetAddress using the provided IP 89 | // and port number. 90 | func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress { 91 | na := &NetAddress{ 92 | IP: ip, 93 | Port: port, 94 | str: net.JoinHostPort( 95 | ip.String(), 96 | strconv.FormatUint(uint64(port), 10), 97 | ), 98 | } 99 | return na 100 | } 101 | 102 | // Equals reports whether na and other are the same addresses. 103 | func (na *NetAddress) Equals(other interface{}) bool { 104 | if o, ok := other.(*NetAddress); ok { 105 | return na.String() == o.String() 106 | } 107 | 108 | return false 109 | } 110 | 111 | func (na *NetAddress) Less(other interface{}) bool { 112 | if o, ok := other.(*NetAddress); ok { 113 | return na.String() < o.String() 114 | } 115 | 116 | cmn.PanicSanity("Cannot compare unequal types") 117 | return false 118 | } 119 | 120 | // String representation. 121 | func (na *NetAddress) String() string { 122 | if na.str == "" { 123 | na.str = net.JoinHostPort( 124 | na.IP.String(), 125 | strconv.FormatUint(uint64(na.Port), 10), 126 | ) 127 | } 128 | return na.str 129 | } 130 | 131 | // Dial calls net.Dial on the address. 132 | func (na *NetAddress) Dial() (net.Conn, error) { 133 | conn, err := net.Dial("tcp", na.String()) 134 | if err != nil { 135 | return nil, err 136 | } 137 | return conn, nil 138 | } 139 | 140 | // DialTimeout calls net.DialTimeout on the address. 141 | func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) { 142 | conn, err := net.DialTimeout("tcp", na.String(), timeout) 143 | if err != nil { 144 | return nil, err 145 | } 146 | return conn, nil 147 | } 148 | 149 | // Routable returns true if the address is routable. 150 | func (na *NetAddress) Routable() bool { 151 | // TODO(oga) bitcoind doesn't include RFC3849 here, but should we? 152 | return na.Valid() && !(na.RFC1918() || na.RFC3927() || na.RFC4862() || 153 | na.RFC4193() || na.RFC4843() || na.Local()) 154 | } 155 | 156 | // For IPv4 these are either a 0 or all bits set address. For IPv6 a zero 157 | // address or one that matches the RFC3849 documentation address format. 158 | func (na *NetAddress) Valid() bool { 159 | return na.IP != nil && !(na.IP.IsUnspecified() || na.RFC3849() || 160 | na.IP.Equal(net.IPv4bcast)) 161 | } 162 | 163 | // Local returns true if it is a local address. 164 | func (na *NetAddress) Local() bool { 165 | return na.IP.IsLoopback() || zero4.Contains(na.IP) 166 | } 167 | 168 | // ReachabilityTo checks whenever o can be reached from na. 169 | func (na *NetAddress) ReachabilityTo(o *NetAddress) int { 170 | const ( 171 | Unreachable = 0 172 | Default = iota 173 | Teredo 174 | Ipv6_weak 175 | Ipv4 176 | Ipv6_strong 177 | Private 178 | ) 179 | if !na.Routable() { 180 | return Unreachable 181 | } else if na.RFC4380() { 182 | if !o.Routable() { 183 | return Default 184 | } else if o.RFC4380() { 185 | return Teredo 186 | } else if o.IP.To4() != nil { 187 | return Ipv4 188 | } else { // ipv6 189 | return Ipv6_weak 190 | } 191 | } else if na.IP.To4() != nil { 192 | if o.Routable() && o.IP.To4() != nil { 193 | return Ipv4 194 | } 195 | return Default 196 | } else /* ipv6 */ { 197 | var tunnelled bool 198 | // Is our v6 is tunnelled? 199 | if o.RFC3964() || o.RFC6052() || o.RFC6145() { 200 | tunnelled = true 201 | } 202 | if !o.Routable() { 203 | return Default 204 | } else if o.RFC4380() { 205 | return Teredo 206 | } else if o.IP.To4() != nil { 207 | return Ipv4 208 | } else if tunnelled { 209 | // only prioritise ipv6 if we aren't tunnelling it. 210 | return Ipv6_weak 211 | } 212 | return Ipv6_strong 213 | } 214 | } 215 | 216 | // RFC1918: IPv4 Private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) 217 | // RFC3849: IPv6 Documentation address (2001:0DB8::/32) 218 | // RFC3927: IPv4 Autoconfig (169.254.0.0/16) 219 | // RFC3964: IPv6 6to4 (2002::/16) 220 | // RFC4193: IPv6 unique local (FC00::/7) 221 | // RFC4380: IPv6 Teredo tunneling (2001::/32) 222 | // RFC4843: IPv6 ORCHID: (2001:10::/28) 223 | // RFC4862: IPv6 Autoconfig (FE80::/64) 224 | // RFC6052: IPv6 well known prefix (64:FF9B::/96) 225 | // RFC6145: IPv6 IPv4 translated address ::FFFF:0:0:0/96 226 | var rfc1918_10 = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)} 227 | var rfc1918_192 = net.IPNet{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)} 228 | var rfc1918_172 = net.IPNet{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)} 229 | var rfc3849 = net.IPNet{IP: net.ParseIP("2001:0DB8::"), Mask: net.CIDRMask(32, 128)} 230 | var rfc3927 = net.IPNet{IP: net.ParseIP("169.254.0.0"), Mask: net.CIDRMask(16, 32)} 231 | var rfc3964 = net.IPNet{IP: net.ParseIP("2002::"), Mask: net.CIDRMask(16, 128)} 232 | var rfc4193 = net.IPNet{IP: net.ParseIP("FC00::"), Mask: net.CIDRMask(7, 128)} 233 | var rfc4380 = net.IPNet{IP: net.ParseIP("2001::"), Mask: net.CIDRMask(32, 128)} 234 | var rfc4843 = net.IPNet{IP: net.ParseIP("2001:10::"), Mask: net.CIDRMask(28, 128)} 235 | var rfc4862 = net.IPNet{IP: net.ParseIP("FE80::"), Mask: net.CIDRMask(64, 128)} 236 | var rfc6052 = net.IPNet{IP: net.ParseIP("64:FF9B::"), Mask: net.CIDRMask(96, 128)} 237 | var rfc6145 = net.IPNet{IP: net.ParseIP("::FFFF:0:0:0"), Mask: net.CIDRMask(96, 128)} 238 | var zero4 = net.IPNet{IP: net.ParseIP("0.0.0.0"), Mask: net.CIDRMask(8, 32)} 239 | 240 | func (na *NetAddress) RFC1918() bool { 241 | return rfc1918_10.Contains(na.IP) || 242 | rfc1918_192.Contains(na.IP) || 243 | rfc1918_172.Contains(na.IP) 244 | } 245 | func (na *NetAddress) RFC3849() bool { return rfc3849.Contains(na.IP) } 246 | func (na *NetAddress) RFC3927() bool { return rfc3927.Contains(na.IP) } 247 | func (na *NetAddress) RFC3964() bool { return rfc3964.Contains(na.IP) } 248 | func (na *NetAddress) RFC4193() bool { return rfc4193.Contains(na.IP) } 249 | func (na *NetAddress) RFC4380() bool { return rfc4380.Contains(na.IP) } 250 | func (na *NetAddress) RFC4843() bool { return rfc4843.Contains(na.IP) } 251 | func (na *NetAddress) RFC4862() bool { return rfc4862.Contains(na.IP) } 252 | func (na *NetAddress) RFC6052() bool { return rfc6052.Contains(na.IP) } 253 | func (na *NetAddress) RFC6145() bool { return rfc6145.Contains(na.IP) } 254 | -------------------------------------------------------------------------------- /netaddress_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestNewNetAddress(t *testing.T) { 12 | assert, require := assert.New(t), require.New(t) 13 | 14 | tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080") 15 | require.Nil(err) 16 | addr := NewNetAddress(tcpAddr) 17 | 18 | assert.Equal("127.0.0.1:8080", addr.String()) 19 | 20 | assert.NotPanics(func() { 21 | NewNetAddress(&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000}) 22 | }, "Calling NewNetAddress with UDPAddr should not panic in testing") 23 | } 24 | 25 | func TestNewNetAddressString(t *testing.T) { 26 | assert, require := assert.New(t), require.New(t) 27 | 28 | tests := []struct { 29 | addr string 30 | correct bool 31 | }{ 32 | {"127.0.0.1:8080", true}, 33 | {"127.0.0:8080", false}, 34 | {"a", false}, 35 | {"127.0.0.1:a", false}, 36 | {"a:8080", false}, 37 | {"8082", false}, 38 | {"127.0.0:8080000", false}, 39 | } 40 | 41 | for _, t := range tests { 42 | addr, err := NewNetAddressString(t.addr) 43 | if t.correct { 44 | require.Nil(err) 45 | assert.Equal(t.addr, addr.String()) 46 | } else { 47 | require.NotNil(err) 48 | } 49 | } 50 | } 51 | 52 | func TestNewNetAddressStrings(t *testing.T) { 53 | assert, require := assert.New(t), require.New(t) 54 | addrs, err := NewNetAddressStrings([]string{"127.0.0.1:8080", "127.0.0.2:8080"}) 55 | require.Nil(err) 56 | 57 | assert.Equal(2, len(addrs)) 58 | } 59 | 60 | func TestNewNetAddressIPPort(t *testing.T) { 61 | assert := assert.New(t) 62 | addr := NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 8080) 63 | 64 | assert.Equal("127.0.0.1:8080", addr.String()) 65 | } 66 | 67 | func TestNetAddressProperties(t *testing.T) { 68 | assert, require := assert.New(t), require.New(t) 69 | 70 | // TODO add more test cases 71 | tests := []struct { 72 | addr string 73 | valid bool 74 | local bool 75 | routable bool 76 | }{ 77 | {"127.0.0.1:8080", true, true, false}, 78 | {"ya.ru:80", true, false, true}, 79 | } 80 | 81 | for _, t := range tests { 82 | addr, err := NewNetAddressString(t.addr) 83 | require.Nil(err) 84 | 85 | assert.Equal(t.valid, addr.Valid()) 86 | assert.Equal(t.local, addr.Local()) 87 | assert.Equal(t.routable, addr.Routable()) 88 | } 89 | } 90 | 91 | func TestNetAddressReachabilityTo(t *testing.T) { 92 | assert, require := assert.New(t), require.New(t) 93 | 94 | // TODO add more test cases 95 | tests := []struct { 96 | addr string 97 | other string 98 | reachability int 99 | }{ 100 | {"127.0.0.1:8080", "127.0.0.1:8081", 0}, 101 | {"ya.ru:80", "127.0.0.1:8080", 1}, 102 | } 103 | 104 | for _, t := range tests { 105 | addr, err := NewNetAddressString(t.addr) 106 | require.Nil(err) 107 | 108 | other, err := NewNetAddressString(t.other) 109 | require.Nil(err) 110 | 111 | assert.Equal(t.reachability, addr.ReachabilityTo(other)) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /peer.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "time" 8 | 9 | "github.com/pkg/errors" 10 | cmn "github.com/tendermint/go-common" 11 | crypto "github.com/tendermint/go-crypto" 12 | wire "github.com/tendermint/go-wire" 13 | ) 14 | 15 | // Peer could be marked as persistent, in which case you can use 16 | // Redial function to reconnect. Note that inbound peers can't be 17 | // made persistent. They should be made persistent on the other end. 18 | // 19 | // Before using a peer, you will need to perform a handshake on connection. 20 | type Peer struct { 21 | cmn.BaseService 22 | 23 | outbound bool 24 | 25 | conn net.Conn // source connection 26 | mconn *MConnection // multiplex connection 27 | 28 | persistent bool 29 | config *PeerConfig 30 | 31 | *NodeInfo 32 | Key string 33 | Data *cmn.CMap // User data. 34 | } 35 | 36 | // PeerConfig is a Peer configuration. 37 | type PeerConfig struct { 38 | AuthEnc bool // authenticated encryption 39 | 40 | HandshakeTimeout time.Duration 41 | DialTimeout time.Duration 42 | 43 | MConfig *MConnConfig 44 | 45 | Fuzz bool // fuzz connection (for testing) 46 | FuzzConfig *FuzzConnConfig 47 | } 48 | 49 | // DefaultPeerConfig returns the default config. 50 | func DefaultPeerConfig() *PeerConfig { 51 | return &PeerConfig{ 52 | AuthEnc: true, 53 | HandshakeTimeout: 2 * time.Second, 54 | DialTimeout: 3 * time.Second, 55 | MConfig: DefaultMConnConfig(), 56 | Fuzz: false, 57 | FuzzConfig: DefaultFuzzConnConfig(), 58 | } 59 | } 60 | 61 | func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519) (*Peer, error) { 62 | return newOutboundPeerWithConfig(addr, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig()) 63 | } 64 | 65 | func newOutboundPeerWithConfig(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) { 66 | conn, err := dial(addr, config) 67 | if err != nil { 68 | return nil, errors.Wrap(err, "Error creating peer") 69 | } 70 | 71 | peer, err := newPeerFromConnAndConfig(conn, true, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config) 72 | if err != nil { 73 | conn.Close() 74 | return nil, err 75 | } 76 | return peer, nil 77 | } 78 | 79 | func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519) (*Peer, error) { 80 | return newInboundPeerWithConfig(conn, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig()) 81 | } 82 | 83 | func newInboundPeerWithConfig(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) { 84 | return newPeerFromConnAndConfig(conn, false, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config) 85 | } 86 | 87 | func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) { 88 | conn := rawConn 89 | 90 | // Fuzz connection 91 | if config.Fuzz { 92 | // so we have time to do peer handshakes and get set up 93 | conn = FuzzConnAfterFromConfig(conn, 10*time.Second, config.FuzzConfig) 94 | } 95 | 96 | // Encrypt connection 97 | if config.AuthEnc { 98 | conn.SetDeadline(time.Now().Add(config.HandshakeTimeout)) 99 | 100 | var err error 101 | conn, err = MakeSecretConnection(conn, ourNodePrivKey) 102 | if err != nil { 103 | return nil, errors.Wrap(err, "Error creating peer") 104 | } 105 | } 106 | 107 | // Key and NodeInfo are set after Handshake 108 | p := &Peer{ 109 | outbound: outbound, 110 | conn: conn, 111 | config: config, 112 | Data: cmn.NewCMap(), 113 | } 114 | 115 | p.mconn = createMConnection(conn, p, reactorsByCh, chDescs, onPeerError, config.MConfig) 116 | 117 | p.BaseService = *cmn.NewBaseService(log, "Peer", p) 118 | 119 | return p, nil 120 | } 121 | 122 | // CloseConn should be used when the peer was created, but never started. 123 | func (p *Peer) CloseConn() { 124 | p.conn.Close() 125 | } 126 | 127 | // makePersistent marks the peer as persistent. 128 | func (p *Peer) makePersistent() { 129 | if !p.outbound { 130 | panic("inbound peers can't be made persistent") 131 | } 132 | 133 | p.persistent = true 134 | } 135 | 136 | // IsPersistent returns true if the peer is persitent, false otherwise. 137 | func (p *Peer) IsPersistent() bool { 138 | return p.persistent 139 | } 140 | 141 | // HandshakeTimeout performs a handshake between a given node and the peer. 142 | // NOTE: blocking 143 | func (p *Peer) HandshakeTimeout(ourNodeInfo *NodeInfo, timeout time.Duration) error { 144 | // Set deadline for handshake so we don't block forever on conn.ReadFull 145 | p.conn.SetDeadline(time.Now().Add(timeout)) 146 | 147 | var peerNodeInfo = new(NodeInfo) 148 | var err1 error 149 | var err2 error 150 | cmn.Parallel( 151 | func() { 152 | var n int 153 | wire.WriteBinary(ourNodeInfo, p.conn, &n, &err1) 154 | }, 155 | func() { 156 | var n int 157 | wire.ReadBinary(peerNodeInfo, p.conn, maxNodeInfoSize, &n, &err2) 158 | log.Notice("Peer handshake", "peerNodeInfo", peerNodeInfo) 159 | }) 160 | if err1 != nil { 161 | return errors.Wrap(err1, "Error during handshake/write") 162 | } 163 | if err2 != nil { 164 | return errors.Wrap(err2, "Error during handshake/read") 165 | } 166 | 167 | if p.config.AuthEnc { 168 | // Check that the professed PubKey matches the sconn's. 169 | if !peerNodeInfo.PubKey.Equals(p.PubKey()) { 170 | return fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v", 171 | peerNodeInfo.PubKey, p.PubKey()) 172 | } 173 | } 174 | 175 | // Remove deadline 176 | p.conn.SetDeadline(time.Time{}) 177 | 178 | peerNodeInfo.RemoteAddr = p.Addr().String() 179 | 180 | p.NodeInfo = peerNodeInfo 181 | p.Key = peerNodeInfo.PubKey.KeyString() 182 | 183 | return nil 184 | } 185 | 186 | // Addr returns peer's network address. 187 | func (p *Peer) Addr() net.Addr { 188 | return p.conn.RemoteAddr() 189 | } 190 | 191 | // PubKey returns peer's public key. 192 | func (p *Peer) PubKey() crypto.PubKeyEd25519 { 193 | if p.config.AuthEnc { 194 | return p.conn.(*SecretConnection).RemotePubKey() 195 | } 196 | if p.NodeInfo == nil { 197 | panic("Attempt to get peer's PubKey before calling Handshake") 198 | } 199 | return p.PubKey() 200 | } 201 | 202 | // OnStart implements BaseService. 203 | func (p *Peer) OnStart() error { 204 | p.BaseService.OnStart() 205 | _, err := p.mconn.Start() 206 | return err 207 | } 208 | 209 | // OnStop implements BaseService. 210 | func (p *Peer) OnStop() { 211 | p.BaseService.OnStop() 212 | p.mconn.Stop() 213 | } 214 | 215 | // Connection returns underlying MConnection. 216 | func (p *Peer) Connection() *MConnection { 217 | return p.mconn 218 | } 219 | 220 | // IsOutbound returns true if the connection is outbound, false otherwise. 221 | func (p *Peer) IsOutbound() bool { 222 | return p.outbound 223 | } 224 | 225 | // Send msg to the channel identified by chID byte. Returns false if the send 226 | // queue is full after timeout, specified by MConnection. 227 | func (p *Peer) Send(chID byte, msg interface{}) bool { 228 | if !p.IsRunning() { 229 | // see Switch#Broadcast, where we fetch the list of peers and loop over 230 | // them - while we're looping, one peer may be removed and stopped. 231 | return false 232 | } 233 | return p.mconn.Send(chID, msg) 234 | } 235 | 236 | // TrySend msg to the channel identified by chID byte. Immediately returns 237 | // false if the send queue is full. 238 | func (p *Peer) TrySend(chID byte, msg interface{}) bool { 239 | if !p.IsRunning() { 240 | return false 241 | } 242 | return p.mconn.TrySend(chID, msg) 243 | } 244 | 245 | // CanSend returns true if the send queue is not full, false otherwise. 246 | func (p *Peer) CanSend(chID byte) bool { 247 | if !p.IsRunning() { 248 | return false 249 | } 250 | return p.mconn.CanSend(chID) 251 | } 252 | 253 | // WriteTo writes the peer's public key to w. 254 | func (p *Peer) WriteTo(w io.Writer) (n int64, err error) { 255 | var n_ int 256 | wire.WriteString(p.Key, w, &n_, &err) 257 | n += int64(n_) 258 | return 259 | } 260 | 261 | // String representation. 262 | func (p *Peer) String() string { 263 | if p.outbound { 264 | return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.Key[:12]) 265 | } 266 | 267 | return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.Key[:12]) 268 | } 269 | 270 | // Equals reports whenever 2 peers are actually represent the same node. 271 | func (p *Peer) Equals(other *Peer) bool { 272 | return p.Key == other.Key 273 | } 274 | 275 | // Get the data for a given key. 276 | func (p *Peer) Get(key string) interface{} { 277 | return p.Data.Get(key) 278 | } 279 | 280 | func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) { 281 | log.Info("Dialing address", "address", addr) 282 | conn, err := addr.DialTimeout(config.DialTimeout) 283 | if err != nil { 284 | log.Info("Failed dialing address", "address", addr, "error", err) 285 | return nil, err 286 | } 287 | return conn, nil 288 | } 289 | 290 | func createMConnection(conn net.Conn, p *Peer, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), config *MConnConfig) *MConnection { 291 | onReceive := func(chID byte, msgBytes []byte) { 292 | reactor := reactorsByCh[chID] 293 | if reactor == nil { 294 | cmn.PanicSanity(cmn.Fmt("Unknown channel %X", chID)) 295 | } 296 | reactor.Receive(chID, p, msgBytes) 297 | } 298 | 299 | onError := func(r interface{}) { 300 | onPeerError(p, r) 301 | } 302 | 303 | return NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config) 304 | } 305 | -------------------------------------------------------------------------------- /peer_set.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // IPeerSet has a (immutable) subset of the methods of PeerSet. 8 | type IPeerSet interface { 9 | Has(key string) bool 10 | Get(key string) *Peer 11 | List() []*Peer 12 | Size() int 13 | } 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | // PeerSet is a special structure for keeping a table of peers. 18 | // Iteration over the peers is super fast and thread-safe. 19 | // We also track how many peers per IP range and avoid too many 20 | type PeerSet struct { 21 | mtx sync.Mutex 22 | lookup map[string]*peerSetItem 23 | list []*Peer 24 | } 25 | 26 | type peerSetItem struct { 27 | peer *Peer 28 | index int 29 | } 30 | 31 | func NewPeerSet() *PeerSet { 32 | return &PeerSet{ 33 | lookup: make(map[string]*peerSetItem), 34 | list: make([]*Peer, 0, 256), 35 | } 36 | } 37 | 38 | // Returns false if peer with key (PubKeyEd25519) is already in set 39 | // or if we have too many peers from the peer's IP range 40 | func (ps *PeerSet) Add(peer *Peer) error { 41 | ps.mtx.Lock() 42 | defer ps.mtx.Unlock() 43 | if ps.lookup[peer.Key] != nil { 44 | return ErrSwitchDuplicatePeer 45 | } 46 | 47 | index := len(ps.list) 48 | // Appending is safe even with other goroutines 49 | // iterating over the ps.list slice. 50 | ps.list = append(ps.list, peer) 51 | ps.lookup[peer.Key] = &peerSetItem{peer, index} 52 | return nil 53 | } 54 | 55 | func (ps *PeerSet) Has(peerKey string) bool { 56 | ps.mtx.Lock() 57 | defer ps.mtx.Unlock() 58 | _, ok := ps.lookup[peerKey] 59 | return ok 60 | } 61 | 62 | func (ps *PeerSet) Get(peerKey string) *Peer { 63 | ps.mtx.Lock() 64 | defer ps.mtx.Unlock() 65 | item, ok := ps.lookup[peerKey] 66 | if ok { 67 | return item.peer 68 | } else { 69 | return nil 70 | } 71 | } 72 | 73 | func (ps *PeerSet) Remove(peer *Peer) { 74 | ps.mtx.Lock() 75 | defer ps.mtx.Unlock() 76 | item := ps.lookup[peer.Key] 77 | if item == nil { 78 | return 79 | } 80 | 81 | index := item.index 82 | // Copy the list but without the last element. 83 | // (we must copy because we're mutating the list) 84 | newList := make([]*Peer, len(ps.list)-1) 85 | copy(newList, ps.list) 86 | // If it's the last peer, that's an easy special case. 87 | if index == len(ps.list)-1 { 88 | ps.list = newList 89 | delete(ps.lookup, peer.Key) 90 | return 91 | } 92 | 93 | // Move the last item from ps.list to "index" in list. 94 | lastPeer := ps.list[len(ps.list)-1] 95 | lastPeerKey := lastPeer.Key 96 | lastPeerItem := ps.lookup[lastPeerKey] 97 | newList[index] = lastPeer 98 | lastPeerItem.index = index 99 | ps.list = newList 100 | delete(ps.lookup, peer.Key) 101 | 102 | } 103 | 104 | func (ps *PeerSet) Size() int { 105 | ps.mtx.Lock() 106 | defer ps.mtx.Unlock() 107 | return len(ps.list) 108 | } 109 | 110 | // threadsafe list of peers. 111 | func (ps *PeerSet) List() []*Peer { 112 | ps.mtx.Lock() 113 | defer ps.mtx.Unlock() 114 | return ps.list 115 | } 116 | -------------------------------------------------------------------------------- /peer_set_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | 7 | . "github.com/tendermint/go-common" 8 | ) 9 | 10 | // Returns an empty dummy peer 11 | func randPeer() *Peer { 12 | return &Peer{ 13 | Key: RandStr(12), 14 | NodeInfo: &NodeInfo{ 15 | RemoteAddr: Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256), 16 | ListenAddr: Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256), 17 | }, 18 | } 19 | } 20 | 21 | func TestAddRemoveOne(t *testing.T) { 22 | peerSet := NewPeerSet() 23 | 24 | peer := randPeer() 25 | err := peerSet.Add(peer) 26 | if err != nil { 27 | t.Errorf("Failed to add new peer") 28 | } 29 | if peerSet.Size() != 1 { 30 | t.Errorf("Failed to add new peer and increment size") 31 | } 32 | 33 | peerSet.Remove(peer) 34 | if peerSet.Has(peer.Key) { 35 | t.Errorf("Failed to remove peer") 36 | } 37 | if peerSet.Size() != 0 { 38 | t.Errorf("Failed to remove peer and decrement size") 39 | } 40 | } 41 | 42 | func TestAddRemoveMany(t *testing.T) { 43 | peerSet := NewPeerSet() 44 | 45 | peers := []*Peer{} 46 | N := 100 47 | for i := 0; i < N; i++ { 48 | peer := randPeer() 49 | if err := peerSet.Add(peer); err != nil { 50 | t.Errorf("Failed to add new peer") 51 | } 52 | if peerSet.Size() != i+1 { 53 | t.Errorf("Failed to add new peer and increment size") 54 | } 55 | peers = append(peers, peer) 56 | } 57 | 58 | for i, peer := range peers { 59 | peerSet.Remove(peer) 60 | if peerSet.Has(peer.Key) { 61 | t.Errorf("Failed to remove peer") 62 | } 63 | if peerSet.Size() != len(peers)-i-1 { 64 | t.Errorf("Failed to remove peer and decrement size") 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /peer_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | golog "log" 5 | "net" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | 12 | crypto "github.com/tendermint/go-crypto" 13 | ) 14 | 15 | func TestPeerBasic(t *testing.T) { 16 | assert, require := assert.New(t), require.New(t) 17 | 18 | // simulate remote peer 19 | rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()} 20 | rp.Start() 21 | defer rp.Stop() 22 | 23 | p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), DefaultPeerConfig()) 24 | require.Nil(err) 25 | 26 | p.Start() 27 | defer p.Stop() 28 | 29 | assert.True(p.IsRunning()) 30 | assert.True(p.IsOutbound()) 31 | assert.False(p.IsPersistent()) 32 | p.makePersistent() 33 | assert.True(p.IsPersistent()) 34 | assert.Equal(rp.Addr().String(), p.Addr().String()) 35 | assert.Equal(rp.PubKey(), p.PubKey()) 36 | } 37 | 38 | func TestPeerWithoutAuthEnc(t *testing.T) { 39 | assert, require := assert.New(t), require.New(t) 40 | 41 | config := DefaultPeerConfig() 42 | config.AuthEnc = false 43 | 44 | // simulate remote peer 45 | rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: config} 46 | rp.Start() 47 | defer rp.Stop() 48 | 49 | p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), config) 50 | require.Nil(err) 51 | 52 | p.Start() 53 | defer p.Stop() 54 | 55 | assert.True(p.IsRunning()) 56 | } 57 | 58 | func TestPeerSend(t *testing.T) { 59 | assert, require := assert.New(t), require.New(t) 60 | 61 | config := DefaultPeerConfig() 62 | config.AuthEnc = false 63 | 64 | // simulate remote peer 65 | rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: config} 66 | rp.Start() 67 | defer rp.Stop() 68 | 69 | p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), config) 70 | require.Nil(err) 71 | 72 | p.Start() 73 | defer p.Stop() 74 | 75 | assert.True(p.CanSend(0x01)) 76 | assert.True(p.Send(0x01, "Asylum")) 77 | } 78 | 79 | func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig) (*Peer, error) { 80 | chDescs := []*ChannelDescriptor{ 81 | &ChannelDescriptor{ID: 0x01, Priority: 1}, 82 | } 83 | reactorsByCh := map[byte]Reactor{0x01: NewTestReactor(chDescs, true)} 84 | pk := crypto.GenPrivKeyEd25519() 85 | p, err := newOutboundPeerWithConfig(addr, reactorsByCh, chDescs, func(p *Peer, r interface{}) {}, pk, config) 86 | if err != nil { 87 | return nil, err 88 | } 89 | err = p.HandshakeTimeout(&NodeInfo{ 90 | PubKey: pk.PubKey().(crypto.PubKeyEd25519), 91 | Moniker: "host_peer", 92 | Network: "testing", 93 | Version: "123.123.123", 94 | }, 1*time.Second) 95 | if err != nil { 96 | return nil, err 97 | } 98 | return p, nil 99 | } 100 | 101 | type remotePeer struct { 102 | PrivKey crypto.PrivKeyEd25519 103 | Config *PeerConfig 104 | addr *NetAddress 105 | quit chan struct{} 106 | } 107 | 108 | func (p *remotePeer) Addr() *NetAddress { 109 | return p.addr 110 | } 111 | 112 | func (p *remotePeer) PubKey() crypto.PubKeyEd25519 { 113 | return p.PrivKey.PubKey().(crypto.PubKeyEd25519) 114 | } 115 | 116 | func (p *remotePeer) Start() { 117 | l, e := net.Listen("tcp", "127.0.0.1:0") // any available address 118 | if e != nil { 119 | golog.Fatalf("net.Listen tcp :0: %+v", e) 120 | } 121 | p.addr = NewNetAddress(l.Addr()) 122 | p.quit = make(chan struct{}) 123 | go p.accept(l) 124 | } 125 | 126 | func (p *remotePeer) Stop() { 127 | close(p.quit) 128 | } 129 | 130 | func (p *remotePeer) accept(l net.Listener) { 131 | for { 132 | conn, err := l.Accept() 133 | if err != nil { 134 | golog.Fatalf("Failed to accept conn: %+v", err) 135 | } 136 | peer, err := newInboundPeerWithConfig(conn, make(map[byte]Reactor), make([]*ChannelDescriptor, 0), func(p *Peer, r interface{}) {}, p.PrivKey, p.Config) 137 | if err != nil { 138 | golog.Fatalf("Failed to create a peer: %+v", err) 139 | } 140 | err = peer.HandshakeTimeout(&NodeInfo{ 141 | PubKey: p.PrivKey.PubKey().(crypto.PubKeyEd25519), 142 | Moniker: "remote_peer", 143 | Network: "testing", 144 | Version: "123.123.123", 145 | }, 1*time.Second) 146 | if err != nil { 147 | golog.Fatalf("Failed to perform handshake: %+v", err) 148 | } 149 | select { 150 | case <-p.quit: 151 | conn.Close() 152 | return 153 | default: 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /pex_reactor.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/rand" 7 | "reflect" 8 | "time" 9 | 10 | cmn "github.com/tendermint/go-common" 11 | wire "github.com/tendermint/go-wire" 12 | ) 13 | 14 | const ( 15 | // PexChannel is a channel for PEX messages 16 | PexChannel = byte(0x00) 17 | 18 | // period to ensure peers connected 19 | defaultEnsurePeersPeriod = 30 * time.Second 20 | minNumOutboundPeers = 10 21 | maxPexMessageSize = 1048576 // 1MB 22 | 23 | // maximum messages one peer can send to us during `msgCountByPeerFlushInterval` 24 | defaultMaxMsgCountByPeer = 1000 25 | msgCountByPeerFlushInterval = 1 * time.Hour 26 | ) 27 | 28 | // PEXReactor handles PEX (peer exchange) and ensures that an 29 | // adequate number of peers are connected to the switch. 30 | // 31 | // It uses `AddrBook` (address book) to store `NetAddress`es of the peers. 32 | // 33 | // ## Preventing abuse 34 | // 35 | // For now, it just limits the number of messages from one peer to 36 | // `defaultMaxMsgCountByPeer` messages per `msgCountByPeerFlushInterval` (1000 37 | // msg/hour). 38 | // 39 | // NOTE [2017-01-17]: 40 | // Limiting is fine for now. Maybe down the road we want to keep track of the 41 | // quality of peer messages so if peerA keeps telling us about peers we can't 42 | // connect to then maybe we should care less about peerA. But I don't think 43 | // that kind of complexity is priority right now. 44 | type PEXReactor struct { 45 | BaseReactor 46 | 47 | sw *Switch 48 | book *AddrBook 49 | ensurePeersPeriod time.Duration 50 | 51 | // tracks message count by peer, so we can prevent abuse 52 | msgCountByPeer *cmn.CMap 53 | maxMsgCountByPeer uint16 54 | } 55 | 56 | // NewPEXReactor creates new PEX reactor. 57 | func NewPEXReactor(b *AddrBook) *PEXReactor { 58 | r := &PEXReactor{ 59 | book: b, 60 | ensurePeersPeriod: defaultEnsurePeersPeriod, 61 | msgCountByPeer: cmn.NewCMap(), 62 | maxMsgCountByPeer: defaultMaxMsgCountByPeer, 63 | } 64 | r.BaseReactor = *NewBaseReactor(log, "PEXReactor", r) 65 | return r 66 | } 67 | 68 | // OnStart implements BaseService 69 | func (r *PEXReactor) OnStart() error { 70 | r.BaseReactor.OnStart() 71 | r.book.Start() 72 | go r.ensurePeersRoutine() 73 | go r.flushMsgCountByPeer() 74 | return nil 75 | } 76 | 77 | // OnStop implements BaseService 78 | func (r *PEXReactor) OnStop() { 79 | r.BaseReactor.OnStop() 80 | r.book.Stop() 81 | } 82 | 83 | // GetChannels implements Reactor 84 | func (r *PEXReactor) GetChannels() []*ChannelDescriptor { 85 | return []*ChannelDescriptor{ 86 | &ChannelDescriptor{ 87 | ID: PexChannel, 88 | Priority: 1, 89 | SendQueueCapacity: 10, 90 | }, 91 | } 92 | } 93 | 94 | // AddPeer implements Reactor by adding peer to the address book (if inbound) 95 | // or by requesting more addresses (if outbound). 96 | func (r *PEXReactor) AddPeer(p *Peer) { 97 | if p.IsOutbound() { 98 | // For outbound peers, the address is already in the books. 99 | // Either it was added in DialSeeds or when we 100 | // received the peer's address in r.Receive 101 | if r.book.NeedMoreAddrs() { 102 | r.RequestPEX(p) 103 | } 104 | } else { // For inbound connections, the peer is its own source 105 | addr, err := NewNetAddressString(p.ListenAddr) 106 | if err != nil { 107 | // this should never happen 108 | log.Error("Error in AddPeer: invalid peer address", "addr", p.ListenAddr, "error", err) 109 | return 110 | } 111 | r.book.AddAddress(addr, addr) 112 | } 113 | } 114 | 115 | // RemovePeer implements Reactor. 116 | func (r *PEXReactor) RemovePeer(p *Peer, reason interface{}) { 117 | // If we aren't keeping track of local temp data for each peer here, then we 118 | // don't have to do anything. 119 | } 120 | 121 | // Receive implements Reactor by handling incoming PEX messages. 122 | func (r *PEXReactor) Receive(chID byte, src *Peer, msgBytes []byte) { 123 | srcAddr := src.Connection().RemoteAddress 124 | srcAddrStr := srcAddr.String() 125 | 126 | r.IncrementMsgCountForPeer(srcAddrStr) 127 | if r.ReachedMaxMsgCountForPeer(srcAddrStr) { 128 | log.Warn("Maximum number of messages reached for peer", "peer", srcAddrStr) 129 | // TODO remove src from peers? 130 | return 131 | } 132 | 133 | _, msg, err := DecodeMessage(msgBytes) 134 | if err != nil { 135 | log.Warn("Error decoding message", "error", err) 136 | return 137 | } 138 | log.Notice("Received message", "msg", msg) 139 | 140 | switch msg := msg.(type) { 141 | case *pexRequestMessage: 142 | // src requested some peers. 143 | r.SendAddrs(src, r.book.GetSelection()) 144 | case *pexAddrsMessage: 145 | // We received some peer addresses from src. 146 | // (We don't want to get spammed with bad peers) 147 | for _, addr := range msg.Addrs { 148 | if addr != nil { 149 | r.book.AddAddress(addr, srcAddr) 150 | } 151 | } 152 | default: 153 | log.Warn(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) 154 | } 155 | } 156 | 157 | // RequestPEX asks peer for more addresses. 158 | func (r *PEXReactor) RequestPEX(p *Peer) { 159 | p.Send(PexChannel, struct{ PexMessage }{&pexRequestMessage{}}) 160 | } 161 | 162 | // SendAddrs sends addrs to the peer. 163 | func (r *PEXReactor) SendAddrs(p *Peer, addrs []*NetAddress) { 164 | p.Send(PexChannel, struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}}) 165 | } 166 | 167 | // SetEnsurePeersPeriod sets period to ensure peers connected. 168 | func (r *PEXReactor) SetEnsurePeersPeriod(d time.Duration) { 169 | r.ensurePeersPeriod = d 170 | } 171 | 172 | // SetMaxMsgCountByPeer sets maximum messages one peer can send to us during 'msgCountByPeerFlushInterval'. 173 | func (r *PEXReactor) SetMaxMsgCountByPeer(v uint16) { 174 | r.maxMsgCountByPeer = v 175 | } 176 | 177 | // ReachedMaxMsgCountForPeer returns true if we received too many 178 | // messages from peer with address `addr`. 179 | // NOTE: assumes the value in the CMap is non-nil 180 | func (r *PEXReactor) ReachedMaxMsgCountForPeer(addr string) bool { 181 | return r.msgCountByPeer.Get(addr).(uint16) >= r.maxMsgCountByPeer 182 | } 183 | 184 | // Increment or initialize the msg count for the peer in the CMap 185 | func (r *PEXReactor) IncrementMsgCountForPeer(addr string) { 186 | var count uint16 187 | countI := r.msgCountByPeer.Get(addr) 188 | if countI != nil { 189 | count = countI.(uint16) 190 | } 191 | count++ 192 | r.msgCountByPeer.Set(addr, count) 193 | } 194 | 195 | // Ensures that sufficient peers are connected. (continuous) 196 | func (r *PEXReactor) ensurePeersRoutine() { 197 | // Randomize when routine starts 198 | ensurePeersPeriodMs := r.ensurePeersPeriod.Nanoseconds() / 1e6 199 | time.Sleep(time.Duration(rand.Int63n(ensurePeersPeriodMs)) * time.Millisecond) 200 | 201 | // fire once immediately. 202 | r.ensurePeers() 203 | 204 | // fire periodically 205 | ticker := time.NewTicker(r.ensurePeersPeriod) 206 | 207 | for { 208 | select { 209 | case <-ticker.C: 210 | r.ensurePeers() 211 | case <-r.Quit: 212 | ticker.Stop() 213 | return 214 | } 215 | } 216 | } 217 | 218 | // ensurePeers ensures that sufficient peers are connected. (once) 219 | // 220 | // Old bucket / New bucket are arbitrary categories to denote whether an 221 | // address is vetted or not, and this needs to be determined over time via a 222 | // heuristic that we haven't perfected yet, or, perhaps is manually edited by 223 | // the node operator. It should not be used to compute what addresses are 224 | // already connected or not. 225 | // 226 | // TODO Basically, we need to work harder on our good-peer/bad-peer marking. 227 | // What we're currently doing in terms of marking good/bad peers is just a 228 | // placeholder. It should not be the case that an address becomes old/vetted 229 | // upon a single successful connection. 230 | func (r *PEXReactor) ensurePeers() { 231 | numOutPeers, _, numDialing := r.Switch.NumPeers() 232 | numToDial := minNumOutboundPeers - (numOutPeers + numDialing) 233 | log.Info("Ensure peers", "numOutPeers", numOutPeers, "numDialing", numDialing, "numToDial", numToDial) 234 | if numToDial <= 0 { 235 | return 236 | } 237 | 238 | toDial := make(map[string]*NetAddress) 239 | 240 | // Try to pick numToDial addresses to dial. 241 | for i := 0; i < numToDial; i++ { 242 | // The purpose of newBias is to first prioritize old (more vetted) peers 243 | // when we have few connections, but to allow for new (less vetted) peers 244 | // if we already have many connections. This algorithm isn't perfect, but 245 | // it somewhat ensures that we prioritize connecting to more-vetted 246 | // peers. 247 | newBias := cmn.MinInt(numOutPeers, 8)*10 + 10 248 | var picked *NetAddress 249 | // Try to fetch a new peer 3 times. 250 | // This caps the maximum number of tries to 3 * numToDial. 251 | for j := 0; j < 3; j++ { 252 | try := r.book.PickAddress(newBias) 253 | if try == nil { 254 | break 255 | } 256 | _, alreadySelected := toDial[try.IP.String()] 257 | alreadyDialing := r.Switch.IsDialing(try) 258 | alreadyConnected := r.Switch.Peers().Has(try.IP.String()) 259 | if alreadySelected || alreadyDialing || alreadyConnected { 260 | // log.Info("Cannot dial address", "addr", try, 261 | // "alreadySelected", alreadySelected, 262 | // "alreadyDialing", alreadyDialing, 263 | // "alreadyConnected", alreadyConnected) 264 | continue 265 | } else { 266 | log.Info("Will dial address", "addr", try) 267 | picked = try 268 | break 269 | } 270 | } 271 | if picked == nil { 272 | continue 273 | } 274 | toDial[picked.IP.String()] = picked 275 | } 276 | 277 | // Dial picked addresses 278 | for _, item := range toDial { 279 | go func(picked *NetAddress) { 280 | _, err := r.Switch.DialPeerWithAddress(picked, false) 281 | if err != nil { 282 | r.book.MarkAttempt(picked) 283 | } 284 | }(item) 285 | } 286 | 287 | // If we need more addresses, pick a random peer and ask for more. 288 | if r.book.NeedMoreAddrs() { 289 | if peers := r.Switch.Peers().List(); len(peers) > 0 { 290 | i := rand.Int() % len(peers) 291 | peer := peers[i] 292 | log.Info("No addresses to dial. Sending pexRequest to random peer", "peer", peer) 293 | r.RequestPEX(peer) 294 | } 295 | } 296 | } 297 | 298 | func (r *PEXReactor) flushMsgCountByPeer() { 299 | ticker := time.NewTicker(msgCountByPeerFlushInterval) 300 | 301 | for { 302 | select { 303 | case <-ticker.C: 304 | r.msgCountByPeer.Clear() 305 | case <-r.Quit: 306 | ticker.Stop() 307 | return 308 | } 309 | } 310 | } 311 | 312 | //----------------------------------------------------------------------------- 313 | // Messages 314 | 315 | const ( 316 | msgTypeRequest = byte(0x01) 317 | msgTypeAddrs = byte(0x02) 318 | ) 319 | 320 | // PexMessage is a primary type for PEX messages. Underneath, it could contain 321 | // either pexRequestMessage, or pexAddrsMessage messages. 322 | type PexMessage interface{} 323 | 324 | var _ = wire.RegisterInterface( 325 | struct{ PexMessage }{}, 326 | wire.ConcreteType{&pexRequestMessage{}, msgTypeRequest}, 327 | wire.ConcreteType{&pexAddrsMessage{}, msgTypeAddrs}, 328 | ) 329 | 330 | // DecodeMessage implements interface registered above. 331 | func DecodeMessage(bz []byte) (msgType byte, msg PexMessage, err error) { 332 | msgType = bz[0] 333 | n := new(int) 334 | r := bytes.NewReader(bz) 335 | msg = wire.ReadBinary(struct{ PexMessage }{}, r, maxPexMessageSize, n, &err).(struct{ PexMessage }).PexMessage 336 | return 337 | } 338 | 339 | /* 340 | A pexRequestMessage requests additional peer addresses. 341 | */ 342 | type pexRequestMessage struct { 343 | } 344 | 345 | func (m *pexRequestMessage) String() string { 346 | return "[pexRequest]" 347 | } 348 | 349 | /* 350 | A message with announced peer addresses. 351 | */ 352 | type pexAddrsMessage struct { 353 | Addrs []*NetAddress 354 | } 355 | 356 | func (m *pexAddrsMessage) String() string { 357 | return fmt.Sprintf("[pexAddrs %v]", m.Addrs) 358 | } 359 | -------------------------------------------------------------------------------- /pex_reactor_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "io/ioutil" 5 | "math/rand" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | cmn "github.com/tendermint/go-common" 13 | wire "github.com/tendermint/go-wire" 14 | ) 15 | 16 | func TestPEXReactorBasic(t *testing.T) { 17 | assert, require := assert.New(t), require.New(t) 18 | 19 | dir, err := ioutil.TempDir("", "pex_reactor") 20 | require.Nil(err) 21 | defer os.RemoveAll(dir) 22 | book := NewAddrBook(dir+"addrbook.json", true) 23 | 24 | r := NewPEXReactor(book) 25 | 26 | assert.NotNil(r) 27 | assert.NotEmpty(r.GetChannels()) 28 | } 29 | 30 | func TestPEXReactorAddRemovePeer(t *testing.T) { 31 | assert, require := assert.New(t), require.New(t) 32 | 33 | dir, err := ioutil.TempDir("", "pex_reactor") 34 | require.Nil(err) 35 | defer os.RemoveAll(dir) 36 | book := NewAddrBook(dir+"addrbook.json", true) 37 | 38 | r := NewPEXReactor(book) 39 | 40 | size := book.Size() 41 | peer := createRandomPeer(false) 42 | 43 | r.AddPeer(peer) 44 | assert.Equal(size+1, book.Size()) 45 | 46 | r.RemovePeer(peer, "peer not available") 47 | assert.Equal(size+1, book.Size()) 48 | 49 | outboundPeer := createRandomPeer(true) 50 | 51 | r.AddPeer(outboundPeer) 52 | assert.Equal(size+1, book.Size(), "outbound peers should not be added to the address book") 53 | 54 | r.RemovePeer(outboundPeer, "peer not available") 55 | assert.Equal(size+1, book.Size()) 56 | } 57 | 58 | func TestPEXReactorRunning(t *testing.T) { 59 | require := require.New(t) 60 | 61 | N := 3 62 | switches := make([]*Switch, N) 63 | 64 | dir, err := ioutil.TempDir("", "pex_reactor") 65 | require.Nil(err) 66 | defer os.RemoveAll(dir) 67 | book := NewAddrBook(dir+"addrbook.json", false) 68 | 69 | // create switches 70 | for i := 0; i < N; i++ { 71 | switches[i] = makeSwitch(i, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch { 72 | r := NewPEXReactor(book) 73 | r.SetEnsurePeersPeriod(250 * time.Millisecond) 74 | sw.AddReactor("pex", r) 75 | return sw 76 | }) 77 | } 78 | 79 | // fill the address book and add listeners 80 | for _, s := range switches { 81 | addr, _ := NewNetAddressString(s.NodeInfo().ListenAddr) 82 | book.AddAddress(addr, addr) 83 | s.AddListener(NewDefaultListener("tcp", s.NodeInfo().ListenAddr, true)) 84 | } 85 | 86 | // start switches 87 | for _, s := range switches { 88 | _, err := s.Start() // start switch and reactors 89 | require.Nil(err) 90 | } 91 | 92 | time.Sleep(1 * time.Second) 93 | 94 | // check peers are connected after some time 95 | for _, s := range switches { 96 | outbound, inbound, _ := s.NumPeers() 97 | if outbound+inbound == 0 { 98 | t.Errorf("%v expected to be connected to at least one peer", s.NodeInfo().ListenAddr) 99 | } 100 | } 101 | 102 | // stop them 103 | for _, s := range switches { 104 | s.Stop() 105 | } 106 | } 107 | 108 | func TestPEXReactorReceive(t *testing.T) { 109 | assert, require := assert.New(t), require.New(t) 110 | 111 | dir, err := ioutil.TempDir("", "pex_reactor") 112 | require.Nil(err) 113 | defer os.RemoveAll(dir) 114 | book := NewAddrBook(dir+"addrbook.json", true) 115 | 116 | r := NewPEXReactor(book) 117 | 118 | peer := createRandomPeer(false) 119 | 120 | size := book.Size() 121 | netAddr, _ := NewNetAddressString(peer.ListenAddr) 122 | addrs := []*NetAddress{netAddr} 123 | msg := wire.BinaryBytes(struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}}) 124 | r.Receive(PexChannel, peer, msg) 125 | assert.Equal(size+1, book.Size()) 126 | 127 | msg = wire.BinaryBytes(struct{ PexMessage }{&pexRequestMessage{}}) 128 | r.Receive(PexChannel, peer, msg) 129 | } 130 | 131 | func TestPEXReactorAbuseFromPeer(t *testing.T) { 132 | assert, require := assert.New(t), require.New(t) 133 | 134 | dir, err := ioutil.TempDir("", "pex_reactor") 135 | require.Nil(err) 136 | defer os.RemoveAll(dir) 137 | book := NewAddrBook(dir+"addrbook.json", true) 138 | 139 | r := NewPEXReactor(book) 140 | r.SetMaxMsgCountByPeer(5) 141 | 142 | peer := createRandomPeer(false) 143 | 144 | msg := wire.BinaryBytes(struct{ PexMessage }{&pexRequestMessage{}}) 145 | for i := 0; i < 10; i++ { 146 | r.Receive(PexChannel, peer, msg) 147 | } 148 | 149 | assert.True(r.ReachedMaxMsgCountForPeer(peer.ListenAddr)) 150 | } 151 | 152 | func createRandomPeer(outbound bool) *Peer { 153 | addr := cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256) 154 | netAddr, _ := NewNetAddressString(addr) 155 | return &Peer{ 156 | Key: cmn.RandStr(12), 157 | NodeInfo: &NodeInfo{ 158 | ListenAddr: addr, 159 | }, 160 | outbound: outbound, 161 | mconn: &MConnection{RemoteAddress: netAddr}, 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /secret_connection.go: -------------------------------------------------------------------------------- 1 | // Uses nacl's secret_box to encrypt a net.Conn. 2 | // It is (meant to be) an implementation of the STS protocol. 3 | // Note we do not (yet) assume that a remote peer's pubkey 4 | // is known ahead of time, and thus we are technically 5 | // still vulnerable to MITM. (TODO!) 6 | // See docs/sts-final.pdf for more info 7 | package p2p 8 | 9 | import ( 10 | "bytes" 11 | crand "crypto/rand" 12 | "crypto/sha256" 13 | "encoding/binary" 14 | "errors" 15 | "io" 16 | "net" 17 | "time" 18 | 19 | "golang.org/x/crypto/nacl/box" 20 | "golang.org/x/crypto/nacl/secretbox" 21 | "golang.org/x/crypto/ripemd160" 22 | 23 | . "github.com/tendermint/go-common" 24 | "github.com/tendermint/go-crypto" 25 | "github.com/tendermint/go-wire" 26 | ) 27 | 28 | // 2 + 1024 == 1026 total frame size 29 | const dataLenSize = 2 // uint16 to describe the length, is <= dataMaxSize 30 | const dataMaxSize = 1024 31 | const totalFrameSize = dataMaxSize + dataLenSize 32 | const sealedFrameSize = totalFrameSize + secretbox.Overhead 33 | const authSigMsgSize = (32 + 1) + (64 + 1) // fixed size (length prefixed) byte arrays 34 | 35 | // Implements net.Conn 36 | type SecretConnection struct { 37 | conn io.ReadWriteCloser 38 | recvBuffer []byte 39 | recvNonce *[24]byte 40 | sendNonce *[24]byte 41 | remPubKey crypto.PubKeyEd25519 42 | shrSecret *[32]byte // shared secret 43 | } 44 | 45 | // Performs handshake and returns a new authenticated SecretConnection. 46 | // Returns nil if error in handshake. 47 | // Caller should call conn.Close() 48 | // See docs/sts-final.pdf for more information. 49 | func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKeyEd25519) (*SecretConnection, error) { 50 | 51 | locPubKey := locPrivKey.PubKey().(crypto.PubKeyEd25519) 52 | 53 | // Generate ephemeral keys for perfect forward secrecy. 54 | locEphPub, locEphPriv := genEphKeys() 55 | 56 | // Write local ephemeral pubkey and receive one too. 57 | // NOTE: every 32-byte string is accepted as a Curve25519 public key 58 | // (see DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf) 59 | remEphPub, err := shareEphPubKey(conn, locEphPub) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | // Compute common shared secret. 65 | shrSecret := computeSharedSecret(remEphPub, locEphPriv) 66 | 67 | // Sort by lexical order. 68 | loEphPub, hiEphPub := sort32(locEphPub, remEphPub) 69 | 70 | // Generate nonces to use for secretbox. 71 | recvNonce, sendNonce := genNonces(loEphPub, hiEphPub, locEphPub == loEphPub) 72 | 73 | // Generate common challenge to sign. 74 | challenge := genChallenge(loEphPub, hiEphPub) 75 | 76 | // Construct SecretConnection. 77 | sc := &SecretConnection{ 78 | conn: conn, 79 | recvBuffer: nil, 80 | recvNonce: recvNonce, 81 | sendNonce: sendNonce, 82 | shrSecret: shrSecret, 83 | } 84 | 85 | // Sign the challenge bytes for authentication. 86 | locSignature := signChallenge(challenge, locPrivKey) 87 | 88 | // Share (in secret) each other's pubkey & challenge signature 89 | authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature) 90 | if err != nil { 91 | return nil, err 92 | } 93 | remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig 94 | if !remPubKey.VerifyBytes(challenge[:], remSignature) { 95 | return nil, errors.New("Challenge verification failed") 96 | } 97 | 98 | // We've authorized. 99 | sc.remPubKey = remPubKey.(crypto.PubKeyEd25519) 100 | return sc, nil 101 | } 102 | 103 | // Returns authenticated remote pubkey 104 | func (sc *SecretConnection) RemotePubKey() crypto.PubKeyEd25519 { 105 | return sc.remPubKey 106 | } 107 | 108 | // Writes encrypted frames of `sealedFrameSize` 109 | // CONTRACT: data smaller than dataMaxSize is read atomically. 110 | func (sc *SecretConnection) Write(data []byte) (n int, err error) { 111 | for 0 < len(data) { 112 | var frame []byte = make([]byte, totalFrameSize) 113 | var chunk []byte 114 | if dataMaxSize < len(data) { 115 | chunk = data[:dataMaxSize] 116 | data = data[dataMaxSize:] 117 | } else { 118 | chunk = data 119 | data = nil 120 | } 121 | chunkLength := len(chunk) 122 | binary.BigEndian.PutUint16(frame, uint16(chunkLength)) 123 | copy(frame[dataLenSize:], chunk) 124 | 125 | // encrypt the frame 126 | var sealedFrame = make([]byte, sealedFrameSize) 127 | secretbox.Seal(sealedFrame[:0], frame, sc.sendNonce, sc.shrSecret) 128 | // fmt.Printf("secretbox.Seal(sealed:%X,sendNonce:%X,shrSecret:%X\n", sealedFrame, sc.sendNonce, sc.shrSecret) 129 | incr2Nonce(sc.sendNonce) 130 | // end encryption 131 | 132 | _, err := sc.conn.Write(sealedFrame) 133 | if err != nil { 134 | return n, err 135 | } else { 136 | n += len(chunk) 137 | } 138 | } 139 | return 140 | } 141 | 142 | // CONTRACT: data smaller than dataMaxSize is read atomically. 143 | func (sc *SecretConnection) Read(data []byte) (n int, err error) { 144 | if 0 < len(sc.recvBuffer) { 145 | n_ := copy(data, sc.recvBuffer) 146 | sc.recvBuffer = sc.recvBuffer[n_:] 147 | return 148 | } 149 | 150 | sealedFrame := make([]byte, sealedFrameSize) 151 | _, err = io.ReadFull(sc.conn, sealedFrame) 152 | if err != nil { 153 | return 154 | } 155 | 156 | // decrypt the frame 157 | var frame = make([]byte, totalFrameSize) 158 | // fmt.Printf("secretbox.Open(sealed:%X,recvNonce:%X,shrSecret:%X\n", sealedFrame, sc.recvNonce, sc.shrSecret) 159 | _, ok := secretbox.Open(frame[:0], sealedFrame, sc.recvNonce, sc.shrSecret) 160 | if !ok { 161 | return n, errors.New("Failed to decrypt SecretConnection") 162 | } 163 | incr2Nonce(sc.recvNonce) 164 | // end decryption 165 | 166 | var chunkLength = binary.BigEndian.Uint16(frame) // read the first two bytes 167 | if chunkLength > dataMaxSize { 168 | return 0, errors.New("chunkLength is greater than dataMaxSize") 169 | } 170 | var chunk = frame[dataLenSize : dataLenSize+chunkLength] 171 | 172 | n = copy(data, chunk) 173 | sc.recvBuffer = chunk[n:] 174 | return 175 | } 176 | 177 | // Implements net.Conn 178 | func (sc *SecretConnection) Close() error { return sc.conn.Close() } 179 | func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() } 180 | func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() } 181 | func (sc *SecretConnection) SetDeadline(t time.Time) error { return sc.conn.(net.Conn).SetDeadline(t) } 182 | func (sc *SecretConnection) SetReadDeadline(t time.Time) error { 183 | return sc.conn.(net.Conn).SetReadDeadline(t) 184 | } 185 | func (sc *SecretConnection) SetWriteDeadline(t time.Time) error { 186 | return sc.conn.(net.Conn).SetWriteDeadline(t) 187 | } 188 | 189 | func genEphKeys() (ephPub, ephPriv *[32]byte) { 190 | var err error 191 | ephPub, ephPriv, err = box.GenerateKey(crand.Reader) 192 | if err != nil { 193 | PanicCrisis("Could not generate ephemeral keypairs") 194 | } 195 | return 196 | } 197 | 198 | func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[32]byte, err error) { 199 | var err1, err2 error 200 | 201 | Parallel( 202 | func() { 203 | _, err1 = conn.Write(locEphPub[:]) 204 | }, 205 | func() { 206 | remEphPub = new([32]byte) 207 | _, err2 = io.ReadFull(conn, remEphPub[:]) 208 | }, 209 | ) 210 | 211 | if err1 != nil { 212 | return nil, err1 213 | } 214 | if err2 != nil { 215 | return nil, err2 216 | } 217 | 218 | return remEphPub, nil 219 | } 220 | 221 | func computeSharedSecret(remPubKey, locPrivKey *[32]byte) (shrSecret *[32]byte) { 222 | shrSecret = new([32]byte) 223 | box.Precompute(shrSecret, remPubKey, locPrivKey) 224 | return 225 | } 226 | 227 | func sort32(foo, bar *[32]byte) (lo, hi *[32]byte) { 228 | if bytes.Compare(foo[:], bar[:]) < 0 { 229 | lo = foo 230 | hi = bar 231 | } else { 232 | lo = bar 233 | hi = foo 234 | } 235 | return 236 | } 237 | 238 | func genNonces(loPubKey, hiPubKey *[32]byte, locIsLo bool) (recvNonce, sendNonce *[24]byte) { 239 | nonce1 := hash24(append(loPubKey[:], hiPubKey[:]...)) 240 | nonce2 := new([24]byte) 241 | copy(nonce2[:], nonce1[:]) 242 | nonce2[len(nonce2)-1] ^= 0x01 243 | if locIsLo { 244 | recvNonce = nonce1 245 | sendNonce = nonce2 246 | } else { 247 | recvNonce = nonce2 248 | sendNonce = nonce1 249 | } 250 | return 251 | } 252 | 253 | func genChallenge(loPubKey, hiPubKey *[32]byte) (challenge *[32]byte) { 254 | return hash32(append(loPubKey[:], hiPubKey[:]...)) 255 | } 256 | 257 | func signChallenge(challenge *[32]byte, locPrivKey crypto.PrivKeyEd25519) (signature crypto.SignatureEd25519) { 258 | signature = locPrivKey.Sign(challenge[:]).(crypto.SignatureEd25519) 259 | return 260 | } 261 | 262 | type authSigMessage struct { 263 | Key crypto.PubKey 264 | Sig crypto.Signature 265 | } 266 | 267 | func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKeyEd25519, signature crypto.SignatureEd25519) (*authSigMessage, error) { 268 | var recvMsg authSigMessage 269 | var err1, err2 error 270 | 271 | Parallel( 272 | func() { 273 | msgBytes := wire.BinaryBytes(authSigMessage{pubKey, signature}) 274 | _, err1 = sc.Write(msgBytes) 275 | }, 276 | func() { 277 | readBuffer := make([]byte, authSigMsgSize) 278 | _, err2 = io.ReadFull(sc, readBuffer) 279 | if err2 != nil { 280 | return 281 | } 282 | n := int(0) // not used. 283 | recvMsg = wire.ReadBinary(authSigMessage{}, bytes.NewBuffer(readBuffer), authSigMsgSize, &n, &err2).(authSigMessage) 284 | }) 285 | 286 | if err1 != nil { 287 | return nil, err1 288 | } 289 | if err2 != nil { 290 | return nil, err2 291 | } 292 | 293 | return &recvMsg, nil 294 | } 295 | 296 | func verifyChallengeSignature(challenge *[32]byte, remPubKey crypto.PubKeyEd25519, remSignature crypto.SignatureEd25519) bool { 297 | return remPubKey.VerifyBytes(challenge[:], remSignature) 298 | } 299 | 300 | //-------------------------------------------------------------------------------- 301 | 302 | // sha256 303 | func hash32(input []byte) (res *[32]byte) { 304 | hasher := sha256.New() 305 | hasher.Write(input) // does not error 306 | resSlice := hasher.Sum(nil) 307 | res = new([32]byte) 308 | copy(res[:], resSlice) 309 | return 310 | } 311 | 312 | // We only fill in the first 20 bytes with ripemd160 313 | func hash24(input []byte) (res *[24]byte) { 314 | hasher := ripemd160.New() 315 | hasher.Write(input) // does not error 316 | resSlice := hasher.Sum(nil) 317 | res = new([24]byte) 318 | copy(res[:], resSlice) 319 | return 320 | } 321 | 322 | // ripemd160 323 | func hash20(input []byte) (res *[20]byte) { 324 | hasher := ripemd160.New() 325 | hasher.Write(input) // does not error 326 | resSlice := hasher.Sum(nil) 327 | res = new([20]byte) 328 | copy(res[:], resSlice) 329 | return 330 | } 331 | 332 | // increment nonce big-endian by 2 with wraparound. 333 | func incr2Nonce(nonce *[24]byte) { 334 | incrNonce(nonce) 335 | incrNonce(nonce) 336 | } 337 | 338 | // increment nonce big-endian by 1 with wraparound. 339 | func incrNonce(nonce *[24]byte) { 340 | for i := 23; 0 <= i; i-- { 341 | nonce[i] += 1 342 | if nonce[i] != 0 { 343 | return 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /secret_connection_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "testing" 7 | 8 | . "github.com/tendermint/go-common" 9 | "github.com/tendermint/go-crypto" 10 | ) 11 | 12 | type dummyConn struct { 13 | *io.PipeReader 14 | *io.PipeWriter 15 | } 16 | 17 | func (drw dummyConn) Close() (err error) { 18 | err2 := drw.PipeWriter.CloseWithError(io.EOF) 19 | err1 := drw.PipeReader.Close() 20 | if err2 != nil { 21 | return err 22 | } 23 | return err1 24 | } 25 | 26 | // Each returned ReadWriteCloser is akin to a net.Connection 27 | func makeDummyConnPair() (fooConn, barConn dummyConn) { 28 | barReader, fooWriter := io.Pipe() 29 | fooReader, barWriter := io.Pipe() 30 | return dummyConn{fooReader, fooWriter}, dummyConn{barReader, barWriter} 31 | } 32 | 33 | func makeSecretConnPair(tb testing.TB) (fooSecConn, barSecConn *SecretConnection) { 34 | fooConn, barConn := makeDummyConnPair() 35 | fooPrvKey := crypto.GenPrivKeyEd25519() 36 | fooPubKey := fooPrvKey.PubKey().(crypto.PubKeyEd25519) 37 | barPrvKey := crypto.GenPrivKeyEd25519() 38 | barPubKey := barPrvKey.PubKey().(crypto.PubKeyEd25519) 39 | 40 | Parallel( 41 | func() { 42 | var err error 43 | fooSecConn, err = MakeSecretConnection(fooConn, fooPrvKey) 44 | if err != nil { 45 | tb.Errorf("Failed to establish SecretConnection for foo: %v", err) 46 | return 47 | } 48 | remotePubBytes := fooSecConn.RemotePubKey() 49 | if !bytes.Equal(remotePubBytes[:], barPubKey[:]) { 50 | tb.Errorf("Unexpected fooSecConn.RemotePubKey. Expected %v, got %v", 51 | barPubKey, fooSecConn.RemotePubKey()) 52 | } 53 | }, 54 | func() { 55 | var err error 56 | barSecConn, err = MakeSecretConnection(barConn, barPrvKey) 57 | if barSecConn == nil { 58 | tb.Errorf("Failed to establish SecretConnection for bar: %v", err) 59 | return 60 | } 61 | remotePubBytes := barSecConn.RemotePubKey() 62 | if !bytes.Equal(remotePubBytes[:], fooPubKey[:]) { 63 | tb.Errorf("Unexpected barSecConn.RemotePubKey. Expected %v, got %v", 64 | fooPubKey, barSecConn.RemotePubKey()) 65 | } 66 | }) 67 | 68 | return 69 | } 70 | 71 | func TestSecretConnectionHandshake(t *testing.T) { 72 | fooSecConn, barSecConn := makeSecretConnPair(t) 73 | fooSecConn.Close() 74 | barSecConn.Close() 75 | } 76 | 77 | func TestSecretConnectionReadWrite(t *testing.T) { 78 | fooConn, barConn := makeDummyConnPair() 79 | fooWrites, barWrites := []string{}, []string{} 80 | fooReads, barReads := []string{}, []string{} 81 | 82 | // Pre-generate the things to write (for foo & bar) 83 | for i := 0; i < 100; i++ { 84 | fooWrites = append(fooWrites, RandStr((RandInt()%(dataMaxSize*5))+1)) 85 | barWrites = append(barWrites, RandStr((RandInt()%(dataMaxSize*5))+1)) 86 | } 87 | 88 | // A helper that will run with (fooConn, fooWrites, fooReads) and vice versa 89 | genNodeRunner := func(nodeConn dummyConn, nodeWrites []string, nodeReads *[]string) func() { 90 | return func() { 91 | // Node handskae 92 | nodePrvKey := crypto.GenPrivKeyEd25519() 93 | nodeSecretConn, err := MakeSecretConnection(nodeConn, nodePrvKey) 94 | if err != nil { 95 | t.Errorf("Failed to establish SecretConnection for node: %v", err) 96 | return 97 | } 98 | // In parallel, handle reads and writes 99 | Parallel( 100 | func() { 101 | // Node writes 102 | for _, nodeWrite := range nodeWrites { 103 | n, err := nodeSecretConn.Write([]byte(nodeWrite)) 104 | if err != nil { 105 | t.Errorf("Failed to write to nodeSecretConn: %v", err) 106 | return 107 | } 108 | if n != len(nodeWrite) { 109 | t.Errorf("Failed to write all bytes. Expected %v, wrote %v", len(nodeWrite), n) 110 | return 111 | } 112 | } 113 | nodeConn.PipeWriter.Close() 114 | }, 115 | func() { 116 | // Node reads 117 | readBuffer := make([]byte, dataMaxSize) 118 | for { 119 | n, err := nodeSecretConn.Read(readBuffer) 120 | if err == io.EOF { 121 | return 122 | } else if err != nil { 123 | t.Errorf("Failed to read from nodeSecretConn: %v", err) 124 | return 125 | } 126 | *nodeReads = append(*nodeReads, string(readBuffer[:n])) 127 | } 128 | nodeConn.PipeReader.Close() 129 | }) 130 | } 131 | } 132 | 133 | // Run foo & bar in parallel 134 | Parallel( 135 | genNodeRunner(fooConn, fooWrites, &fooReads), 136 | genNodeRunner(barConn, barWrites, &barReads), 137 | ) 138 | 139 | // A helper to ensure that the writes and reads match. 140 | // Additionally, small writes (<= dataMaxSize) must be atomically read. 141 | compareWritesReads := func(writes []string, reads []string) { 142 | for { 143 | // Pop next write & corresponding reads 144 | var read, write string = "", writes[0] 145 | var readCount = 0 146 | for _, readChunk := range reads { 147 | read += readChunk 148 | readCount += 1 149 | if len(write) <= len(read) { 150 | break 151 | } 152 | if len(write) <= dataMaxSize { 153 | break // atomicity of small writes 154 | } 155 | } 156 | // Compare 157 | if write != read { 158 | t.Errorf("Expected to read %X, got %X", write, read) 159 | } 160 | // Iterate 161 | writes = writes[1:] 162 | reads = reads[readCount:] 163 | if len(writes) == 0 { 164 | break 165 | } 166 | } 167 | } 168 | 169 | compareWritesReads(fooWrites, barReads) 170 | compareWritesReads(barWrites, fooReads) 171 | 172 | } 173 | 174 | func BenchmarkSecretConnection(b *testing.B) { 175 | b.StopTimer() 176 | fooSecConn, barSecConn := makeSecretConnPair(b) 177 | fooWriteText := RandStr(dataMaxSize) 178 | // Consume reads from bar's reader 179 | go func() { 180 | readBuffer := make([]byte, dataMaxSize) 181 | for { 182 | _, err := barSecConn.Read(readBuffer) 183 | if err == io.EOF { 184 | return 185 | } else if err != nil { 186 | b.Fatalf("Failed to read from barSecConn: %v", err) 187 | } 188 | } 189 | }() 190 | 191 | b.StartTimer() 192 | for i := 0; i < b.N; i++ { 193 | _, err := fooSecConn.Write([]byte(fooWriteText)) 194 | if err != nil { 195 | b.Fatalf("Failed to write to fooSecConn: %v", err) 196 | } 197 | } 198 | b.StopTimer() 199 | 200 | fooSecConn.Close() 201 | //barSecConn.Close() race condition 202 | } 203 | -------------------------------------------------------------------------------- /switch.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/rand" 7 | "net" 8 | "time" 9 | 10 | . "github.com/tendermint/go-common" 11 | cfg "github.com/tendermint/go-config" 12 | crypto "github.com/tendermint/go-crypto" 13 | "github.com/tendermint/log15" 14 | ) 15 | 16 | const ( 17 | reconnectAttempts = 30 18 | reconnectInterval = 3 * time.Second 19 | ) 20 | 21 | type Reactor interface { 22 | Service // Start, Stop 23 | 24 | SetSwitch(*Switch) 25 | GetChannels() []*ChannelDescriptor 26 | AddPeer(peer *Peer) 27 | RemovePeer(peer *Peer, reason interface{}) 28 | Receive(chID byte, peer *Peer, msgBytes []byte) 29 | } 30 | 31 | //-------------------------------------- 32 | 33 | type BaseReactor struct { 34 | BaseService // Provides Start, Stop, .Quit 35 | Switch *Switch 36 | } 37 | 38 | func NewBaseReactor(log log15.Logger, name string, impl Reactor) *BaseReactor { 39 | return &BaseReactor{ 40 | BaseService: *NewBaseService(log, name, impl), 41 | Switch: nil, 42 | } 43 | } 44 | 45 | func (br *BaseReactor) SetSwitch(sw *Switch) { 46 | br.Switch = sw 47 | } 48 | func (_ *BaseReactor) GetChannels() []*ChannelDescriptor { return nil } 49 | func (_ *BaseReactor) AddPeer(peer *Peer) {} 50 | func (_ *BaseReactor) RemovePeer(peer *Peer, reason interface{}) {} 51 | func (_ *BaseReactor) Receive(chID byte, peer *Peer, msgBytes []byte) {} 52 | 53 | //----------------------------------------------------------------------------- 54 | 55 | /* 56 | The `Switch` handles peer connections and exposes an API to receive incoming messages 57 | on `Reactors`. Each `Reactor` is responsible for handling incoming messages of one 58 | or more `Channels`. So while sending outgoing messages is typically performed on the peer, 59 | incoming messages are received on the reactor. 60 | */ 61 | type Switch struct { 62 | BaseService 63 | 64 | config cfg.Config 65 | listeners []Listener 66 | reactors map[string]Reactor 67 | chDescs []*ChannelDescriptor 68 | reactorsByCh map[byte]Reactor 69 | peers *PeerSet 70 | dialing *CMap 71 | nodeInfo *NodeInfo // our node info 72 | nodePrivKey crypto.PrivKeyEd25519 // our node privkey 73 | 74 | filterConnByAddr func(net.Addr) error 75 | filterConnByPubKey func(crypto.PubKeyEd25519) error 76 | } 77 | 78 | var ( 79 | ErrSwitchDuplicatePeer = errors.New("Duplicate peer") 80 | ErrSwitchMaxPeersPerIPRange = errors.New("IP range has too many peers") 81 | ) 82 | 83 | func NewSwitch(config cfg.Config) *Switch { 84 | setConfigDefaults(config) 85 | 86 | sw := &Switch{ 87 | config: config, 88 | reactors: make(map[string]Reactor), 89 | chDescs: make([]*ChannelDescriptor, 0), 90 | reactorsByCh: make(map[byte]Reactor), 91 | peers: NewPeerSet(), 92 | dialing: NewCMap(), 93 | nodeInfo: nil, 94 | } 95 | sw.BaseService = *NewBaseService(log, "P2P Switch", sw) 96 | return sw 97 | } 98 | 99 | // Not goroutine safe. 100 | func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor { 101 | // Validate the reactor. 102 | // No two reactors can share the same channel. 103 | reactorChannels := reactor.GetChannels() 104 | for _, chDesc := range reactorChannels { 105 | chID := chDesc.ID 106 | if sw.reactorsByCh[chID] != nil { 107 | PanicSanity(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chID, sw.reactorsByCh[chID], reactor)) 108 | } 109 | sw.chDescs = append(sw.chDescs, chDesc) 110 | sw.reactorsByCh[chID] = reactor 111 | } 112 | sw.reactors[name] = reactor 113 | reactor.SetSwitch(sw) 114 | return reactor 115 | } 116 | 117 | // Not goroutine safe. 118 | func (sw *Switch) Reactors() map[string]Reactor { 119 | return sw.reactors 120 | } 121 | 122 | // Not goroutine safe. 123 | func (sw *Switch) Reactor(name string) Reactor { 124 | return sw.reactors[name] 125 | } 126 | 127 | // Not goroutine safe. 128 | func (sw *Switch) AddListener(l Listener) { 129 | sw.listeners = append(sw.listeners, l) 130 | } 131 | 132 | // Not goroutine safe. 133 | func (sw *Switch) Listeners() []Listener { 134 | return sw.listeners 135 | } 136 | 137 | // Not goroutine safe. 138 | func (sw *Switch) IsListening() bool { 139 | return len(sw.listeners) > 0 140 | } 141 | 142 | // Not goroutine safe. 143 | func (sw *Switch) SetNodeInfo(nodeInfo *NodeInfo) { 144 | sw.nodeInfo = nodeInfo 145 | } 146 | 147 | // Not goroutine safe. 148 | func (sw *Switch) NodeInfo() *NodeInfo { 149 | return sw.nodeInfo 150 | } 151 | 152 | // Not goroutine safe. 153 | // NOTE: Overwrites sw.nodeInfo.PubKey 154 | func (sw *Switch) SetNodePrivKey(nodePrivKey crypto.PrivKeyEd25519) { 155 | sw.nodePrivKey = nodePrivKey 156 | if sw.nodeInfo != nil { 157 | sw.nodeInfo.PubKey = nodePrivKey.PubKey().(crypto.PubKeyEd25519) 158 | } 159 | } 160 | 161 | // Switch.Start() starts all the reactors, peers, and listeners. 162 | func (sw *Switch) OnStart() error { 163 | sw.BaseService.OnStart() 164 | // Start reactors 165 | for _, reactor := range sw.reactors { 166 | _, err := reactor.Start() 167 | if err != nil { 168 | return err 169 | } 170 | } 171 | // Start peers 172 | for _, peer := range sw.peers.List() { 173 | sw.startInitPeer(peer) 174 | } 175 | // Start listeners 176 | for _, listener := range sw.listeners { 177 | go sw.listenerRoutine(listener) 178 | } 179 | return nil 180 | } 181 | 182 | func (sw *Switch) OnStop() { 183 | sw.BaseService.OnStop() 184 | // Stop listeners 185 | for _, listener := range sw.listeners { 186 | listener.Stop() 187 | } 188 | sw.listeners = nil 189 | // Stop peers 190 | for _, peer := range sw.peers.List() { 191 | peer.Stop() 192 | sw.peers.Remove(peer) 193 | } 194 | // Stop reactors 195 | for _, reactor := range sw.reactors { 196 | reactor.Stop() 197 | } 198 | } 199 | 200 | // NOTE: This performs a blocking handshake before the peer is added. 201 | // CONTRACT: If error is returned, peer is nil, and conn is immediately closed. 202 | func (sw *Switch) AddPeer(peer *Peer) error { 203 | if err := sw.FilterConnByAddr(peer.Addr()); err != nil { 204 | return err 205 | } 206 | 207 | if err := sw.FilterConnByPubKey(peer.PubKey()); err != nil { 208 | return err 209 | } 210 | 211 | if err := peer.HandshakeTimeout(sw.nodeInfo, time.Duration(sw.config.GetInt(configKeyHandshakeTimeoutSeconds))*time.Second); err != nil { 212 | return err 213 | } 214 | 215 | // Avoid self 216 | if sw.nodeInfo.PubKey.Equals(peer.PubKey()) { 217 | return errors.New("Ignoring connection from self") 218 | } 219 | 220 | // Check version, chain id 221 | if err := sw.nodeInfo.CompatibleWith(peer.NodeInfo); err != nil { 222 | return err 223 | } 224 | 225 | // Add the peer to .peers 226 | // ignore if duplicate or if we already have too many for that IP range 227 | if err := sw.peers.Add(peer); err != nil { 228 | log.Notice("Ignoring peer", "error", err, "peer", peer) 229 | peer.Stop() 230 | return err 231 | } 232 | 233 | // Start peer 234 | if sw.IsRunning() { 235 | sw.startInitPeer(peer) 236 | } 237 | 238 | log.Notice("Added peer", "peer", peer) 239 | return nil 240 | } 241 | 242 | func (sw *Switch) FilterConnByAddr(addr net.Addr) error { 243 | if sw.filterConnByAddr != nil { 244 | return sw.filterConnByAddr(addr) 245 | } 246 | return nil 247 | } 248 | 249 | func (sw *Switch) FilterConnByPubKey(pubkey crypto.PubKeyEd25519) error { 250 | if sw.filterConnByPubKey != nil { 251 | return sw.filterConnByPubKey(pubkey) 252 | } 253 | return nil 254 | 255 | } 256 | 257 | func (sw *Switch) SetAddrFilter(f func(net.Addr) error) { 258 | sw.filterConnByAddr = f 259 | } 260 | 261 | func (sw *Switch) SetPubKeyFilter(f func(crypto.PubKeyEd25519) error) { 262 | sw.filterConnByPubKey = f 263 | } 264 | 265 | func (sw *Switch) startInitPeer(peer *Peer) { 266 | peer.Start() // spawn send/recv routines 267 | for _, reactor := range sw.reactors { 268 | reactor.AddPeer(peer) 269 | } 270 | } 271 | 272 | // Dial a list of seeds asynchronously in random order 273 | func (sw *Switch) DialSeeds(addrBook *AddrBook, seeds []string) error { 274 | 275 | netAddrs, err := NewNetAddressStrings(seeds) 276 | if err != nil { 277 | return err 278 | } 279 | 280 | if addrBook != nil { 281 | // add seeds to `addrBook` 282 | ourAddrS := sw.nodeInfo.ListenAddr 283 | ourAddr, _ := NewNetAddressString(ourAddrS) 284 | for _, netAddr := range netAddrs { 285 | // do not add ourselves 286 | if netAddr.Equals(ourAddr) { 287 | continue 288 | } 289 | addrBook.AddAddress(netAddr, ourAddr) 290 | } 291 | addrBook.Save() 292 | } 293 | 294 | // permute the list, dial them in random order. 295 | perm := rand.Perm(len(netAddrs)) 296 | for i := 0; i < len(perm); i++ { 297 | go func(i int) { 298 | time.Sleep(time.Duration(rand.Int63n(3000)) * time.Millisecond) 299 | j := perm[i] 300 | sw.dialSeed(netAddrs[j]) 301 | }(i) 302 | } 303 | return nil 304 | } 305 | 306 | func (sw *Switch) dialSeed(addr *NetAddress) { 307 | peer, err := sw.DialPeerWithAddress(addr, true) 308 | if err != nil { 309 | log.Error("Error dialing seed", "error", err) 310 | return 311 | } else { 312 | log.Notice("Connected to seed", "peer", peer) 313 | } 314 | } 315 | 316 | func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (*Peer, error) { 317 | sw.dialing.Set(addr.IP.String(), addr) 318 | defer sw.dialing.Delete(addr.IP.String()) 319 | 320 | peer, err := newOutboundPeerWithConfig(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, peerConfigFromGoConfig(sw.config)) 321 | if err != nil { 322 | log.Info("Failed dialing peer", "address", addr, "error", err) 323 | return nil, err 324 | } 325 | if persistent { 326 | peer.makePersistent() 327 | } 328 | err = sw.AddPeer(peer) 329 | if err != nil { 330 | log.Info("Failed adding peer", "address", addr, "error", err) 331 | peer.CloseConn() 332 | return nil, err 333 | } 334 | log.Notice("Dialed and added peer", "address", addr, "peer", peer) 335 | return peer, nil 336 | } 337 | 338 | func (sw *Switch) IsDialing(addr *NetAddress) bool { 339 | return sw.dialing.Has(addr.IP.String()) 340 | } 341 | 342 | // Broadcast runs a go routine for each attempted send, which will block 343 | // trying to send for defaultSendTimeoutSeconds. Returns a channel 344 | // which receives success values for each attempted send (false if times out) 345 | // NOTE: Broadcast uses goroutines, so order of broadcast may not be preserved. 346 | func (sw *Switch) Broadcast(chID byte, msg interface{}) chan bool { 347 | successChan := make(chan bool, len(sw.peers.List())) 348 | log.Debug("Broadcast", "channel", chID, "msg", msg) 349 | for _, peer := range sw.peers.List() { 350 | go func(peer *Peer) { 351 | success := peer.Send(chID, msg) 352 | successChan <- success 353 | }(peer) 354 | } 355 | return successChan 356 | } 357 | 358 | // Returns the count of outbound/inbound and outbound-dialing peers. 359 | func (sw *Switch) NumPeers() (outbound, inbound, dialing int) { 360 | peers := sw.peers.List() 361 | for _, peer := range peers { 362 | if peer.outbound { 363 | outbound++ 364 | } else { 365 | inbound++ 366 | } 367 | } 368 | dialing = sw.dialing.Size() 369 | return 370 | } 371 | 372 | func (sw *Switch) Peers() IPeerSet { 373 | return sw.peers 374 | } 375 | 376 | // Disconnect from a peer due to external error, retry if it is a persistent peer. 377 | // TODO: make record depending on reason. 378 | func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) { 379 | addr := NewNetAddress(peer.Addr()) 380 | log.Notice("Stopping peer for error", "peer", peer, "error", reason) 381 | sw.stopAndRemovePeer(peer, reason) 382 | 383 | if peer.IsPersistent() { 384 | go func() { 385 | log.Notice("Reconnecting to peer", "peer", peer) 386 | for i := 1; i < reconnectAttempts; i++ { 387 | if !sw.IsRunning() { 388 | return 389 | } 390 | 391 | peer, err := sw.DialPeerWithAddress(addr, true) 392 | if err != nil { 393 | if i == reconnectAttempts { 394 | log.Notice("Error reconnecting to peer. Giving up", "tries", i, "error", err) 395 | return 396 | } 397 | log.Notice("Error reconnecting to peer. Trying again", "tries", i, "error", err) 398 | time.Sleep(reconnectInterval) 399 | continue 400 | } 401 | 402 | log.Notice("Reconnected to peer", "peer", peer) 403 | return 404 | } 405 | }() 406 | } 407 | } 408 | 409 | // Disconnect from a peer gracefully. 410 | // TODO: handle graceful disconnects. 411 | func (sw *Switch) StopPeerGracefully(peer *Peer) { 412 | log.Notice("Stopping peer gracefully") 413 | sw.stopAndRemovePeer(peer, nil) 414 | } 415 | 416 | func (sw *Switch) stopAndRemovePeer(peer *Peer, reason interface{}) { 417 | sw.peers.Remove(peer) 418 | peer.Stop() 419 | for _, reactor := range sw.reactors { 420 | reactor.RemovePeer(peer, reason) 421 | } 422 | } 423 | 424 | func (sw *Switch) listenerRoutine(l Listener) { 425 | for { 426 | inConn, ok := <-l.Connections() 427 | if !ok { 428 | break 429 | } 430 | 431 | // ignore connection if we already have enough 432 | maxPeers := sw.config.GetInt(configKeyMaxNumPeers) 433 | if maxPeers <= sw.peers.Size() { 434 | log.Info("Ignoring inbound connection: already have enough peers", "address", inConn.RemoteAddr().String(), "numPeers", sw.peers.Size(), "max", maxPeers) 435 | continue 436 | } 437 | 438 | // New inbound connection! 439 | err := sw.addPeerWithConnectionAndConfig(inConn, peerConfigFromGoConfig(sw.config)) 440 | if err != nil { 441 | log.Notice("Ignoring inbound connection: error while adding peer", "address", inConn.RemoteAddr().String(), "error", err) 442 | continue 443 | } 444 | 445 | // NOTE: We don't yet have the listening port of the 446 | // remote (if they have a listener at all). 447 | // The peerHandshake will handle that 448 | } 449 | 450 | // cleanup 451 | } 452 | 453 | //----------------------------------------------------------------------------- 454 | 455 | type SwitchEventNewPeer struct { 456 | Peer *Peer 457 | } 458 | 459 | type SwitchEventDonePeer struct { 460 | Peer *Peer 461 | Error interface{} 462 | } 463 | 464 | //------------------------------------------------------------------ 465 | // Switches connected via arbitrary net.Conn; useful for testing 466 | 467 | // Returns n switches, connected according to the connect func. 468 | // If connect==Connect2Switches, the switches will be fully connected. 469 | // initSwitch defines how the ith switch should be initialized (ie. with what reactors). 470 | // NOTE: panics if any switch fails to start. 471 | func MakeConnectedSwitches(n int, initSwitch func(int, *Switch) *Switch, connect func([]*Switch, int, int)) []*Switch { 472 | switches := make([]*Switch, n) 473 | for i := 0; i < n; i++ { 474 | switches[i] = makeSwitch(i, "testing", "123.123.123", initSwitch) 475 | } 476 | 477 | if err := StartSwitches(switches); err != nil { 478 | panic(err) 479 | } 480 | 481 | for i := 0; i < n; i++ { 482 | for j := i; j < n; j++ { 483 | connect(switches, i, j) 484 | } 485 | } 486 | 487 | return switches 488 | } 489 | 490 | var PanicOnAddPeerErr = false 491 | 492 | // Will connect switches i and j via net.Pipe() 493 | // Blocks until a conection is established. 494 | // NOTE: caller ensures i and j are within bounds 495 | func Connect2Switches(switches []*Switch, i, j int) { 496 | switchI := switches[i] 497 | switchJ := switches[j] 498 | c1, c2 := net.Pipe() 499 | doneCh := make(chan struct{}) 500 | go func() { 501 | err := switchI.addPeerWithConnection(c1) 502 | if PanicOnAddPeerErr && err != nil { 503 | panic(err) 504 | } 505 | doneCh <- struct{}{} 506 | }() 507 | go func() { 508 | err := switchJ.addPeerWithConnection(c2) 509 | if PanicOnAddPeerErr && err != nil { 510 | panic(err) 511 | } 512 | doneCh <- struct{}{} 513 | }() 514 | <-doneCh 515 | <-doneCh 516 | } 517 | 518 | func StartSwitches(switches []*Switch) error { 519 | for _, s := range switches { 520 | _, err := s.Start() // start switch and reactors 521 | if err != nil { 522 | return err 523 | } 524 | } 525 | return nil 526 | } 527 | 528 | func makeSwitch(i int, network, version string, initSwitch func(int, *Switch) *Switch) *Switch { 529 | privKey := crypto.GenPrivKeyEd25519() 530 | // new switch, add reactors 531 | // TODO: let the config be passed in? 532 | s := initSwitch(i, NewSwitch(cfg.NewMapConfig(nil))) 533 | s.SetNodeInfo(&NodeInfo{ 534 | PubKey: privKey.PubKey().(crypto.PubKeyEd25519), 535 | Moniker: Fmt("switch%d", i), 536 | Network: network, 537 | Version: version, 538 | RemoteAddr: Fmt("%v:%v", network, rand.Intn(64512)+1023), 539 | ListenAddr: Fmt("%v:%v", network, rand.Intn(64512)+1023), 540 | }) 541 | s.SetNodePrivKey(privKey) 542 | return s 543 | } 544 | 545 | func (sw *Switch) addPeerWithConnection(conn net.Conn) error { 546 | peer, err := newInboundPeer(conn, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey) 547 | if err != nil { 548 | conn.Close() 549 | return err 550 | } 551 | 552 | if err = sw.AddPeer(peer); err != nil { 553 | conn.Close() 554 | return err 555 | } 556 | 557 | return nil 558 | } 559 | 560 | func (sw *Switch) addPeerWithConnectionAndConfig(conn net.Conn, config *PeerConfig) error { 561 | peer, err := newInboundPeerWithConfig(conn, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, config) 562 | if err != nil { 563 | conn.Close() 564 | return err 565 | } 566 | 567 | if err = sw.AddPeer(peer); err != nil { 568 | conn.Close() 569 | return err 570 | } 571 | 572 | return nil 573 | } 574 | 575 | func peerConfigFromGoConfig(config cfg.Config) *PeerConfig { 576 | return &PeerConfig{ 577 | AuthEnc: config.GetBool(configKeyAuthEnc), 578 | Fuzz: config.GetBool(configFuzzEnable), 579 | HandshakeTimeout: time.Duration(config.GetInt(configKeyHandshakeTimeoutSeconds)) * time.Second, 580 | DialTimeout: time.Duration(config.GetInt(configKeyDialTimeoutSeconds)) * time.Second, 581 | MConfig: &MConnConfig{ 582 | SendRate: int64(config.GetInt(configKeySendRate)), 583 | RecvRate: int64(config.GetInt(configKeyRecvRate)), 584 | }, 585 | FuzzConfig: &FuzzConnConfig{ 586 | Mode: config.GetInt(configFuzzMode), 587 | MaxDelay: time.Duration(config.GetInt(configFuzzMaxDelayMilliseconds)) * time.Millisecond, 588 | ProbDropRW: config.GetFloat64(configFuzzProbDropRW), 589 | ProbDropConn: config.GetFloat64(configFuzzProbDropConn), 590 | ProbSleep: config.GetFloat64(configFuzzProbSleep), 591 | }, 592 | } 593 | } 594 | -------------------------------------------------------------------------------- /switch_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "net" 7 | "sync" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | . "github.com/tendermint/go-common" 14 | cfg "github.com/tendermint/go-config" 15 | crypto "github.com/tendermint/go-crypto" 16 | wire "github.com/tendermint/go-wire" 17 | ) 18 | 19 | var ( 20 | config cfg.Config 21 | ) 22 | 23 | func init() { 24 | config = cfg.NewMapConfig(nil) 25 | setConfigDefaults(config) 26 | } 27 | 28 | type PeerMessage struct { 29 | PeerKey string 30 | Bytes []byte 31 | Counter int 32 | } 33 | 34 | type TestReactor struct { 35 | BaseReactor 36 | 37 | mtx sync.Mutex 38 | channels []*ChannelDescriptor 39 | peersAdded []*Peer 40 | peersRemoved []*Peer 41 | logMessages bool 42 | msgsCounter int 43 | msgsReceived map[byte][]PeerMessage 44 | } 45 | 46 | func NewTestReactor(channels []*ChannelDescriptor, logMessages bool) *TestReactor { 47 | tr := &TestReactor{ 48 | channels: channels, 49 | logMessages: logMessages, 50 | msgsReceived: make(map[byte][]PeerMessage), 51 | } 52 | tr.BaseReactor = *NewBaseReactor(log, "TestReactor", tr) 53 | return tr 54 | } 55 | 56 | func (tr *TestReactor) GetChannels() []*ChannelDescriptor { 57 | return tr.channels 58 | } 59 | 60 | func (tr *TestReactor) AddPeer(peer *Peer) { 61 | tr.mtx.Lock() 62 | defer tr.mtx.Unlock() 63 | tr.peersAdded = append(tr.peersAdded, peer) 64 | } 65 | 66 | func (tr *TestReactor) RemovePeer(peer *Peer, reason interface{}) { 67 | tr.mtx.Lock() 68 | defer tr.mtx.Unlock() 69 | tr.peersRemoved = append(tr.peersRemoved, peer) 70 | } 71 | 72 | func (tr *TestReactor) Receive(chID byte, peer *Peer, msgBytes []byte) { 73 | if tr.logMessages { 74 | tr.mtx.Lock() 75 | defer tr.mtx.Unlock() 76 | //fmt.Printf("Received: %X, %X\n", chID, msgBytes) 77 | tr.msgsReceived[chID] = append(tr.msgsReceived[chID], PeerMessage{peer.Key, msgBytes, tr.msgsCounter}) 78 | tr.msgsCounter++ 79 | } 80 | } 81 | 82 | func (tr *TestReactor) getMsgs(chID byte) []PeerMessage { 83 | tr.mtx.Lock() 84 | defer tr.mtx.Unlock() 85 | return tr.msgsReceived[chID] 86 | } 87 | 88 | //----------------------------------------------------------------------------- 89 | 90 | // convenience method for creating two switches connected to each other. 91 | // XXX: note this uses net.Pipe and not a proper TCP conn 92 | func makeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switch, *Switch) { 93 | // Create two switches that will be interconnected. 94 | switches := MakeConnectedSwitches(2, initSwitch, Connect2Switches) 95 | return switches[0], switches[1] 96 | } 97 | 98 | func initSwitchFunc(i int, sw *Switch) *Switch { 99 | // Make two reactors of two channels each 100 | sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{ 101 | &ChannelDescriptor{ID: byte(0x00), Priority: 10}, 102 | &ChannelDescriptor{ID: byte(0x01), Priority: 10}, 103 | }, true)) 104 | sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{ 105 | &ChannelDescriptor{ID: byte(0x02), Priority: 10}, 106 | &ChannelDescriptor{ID: byte(0x03), Priority: 10}, 107 | }, true)) 108 | return sw 109 | } 110 | 111 | func TestSwitches(t *testing.T) { 112 | s1, s2 := makeSwitchPair(t, initSwitchFunc) 113 | defer s1.Stop() 114 | defer s2.Stop() 115 | 116 | if s1.Peers().Size() != 1 { 117 | t.Errorf("Expected exactly 1 peer in s1, got %v", s1.Peers().Size()) 118 | } 119 | if s2.Peers().Size() != 1 { 120 | t.Errorf("Expected exactly 1 peer in s2, got %v", s2.Peers().Size()) 121 | } 122 | 123 | // Lets send some messages 124 | ch0Msg := "channel zero" 125 | ch1Msg := "channel foo" 126 | ch2Msg := "channel bar" 127 | 128 | s1.Broadcast(byte(0x00), ch0Msg) 129 | s1.Broadcast(byte(0x01), ch1Msg) 130 | s1.Broadcast(byte(0x02), ch2Msg) 131 | 132 | // Wait for things to settle... 133 | time.Sleep(5000 * time.Millisecond) 134 | 135 | // Check message on ch0 136 | ch0Msgs := s2.Reactor("foo").(*TestReactor).getMsgs(byte(0x00)) 137 | if len(ch0Msgs) != 1 { 138 | t.Errorf("Expected to have received 1 message in ch0") 139 | } 140 | if !bytes.Equal(ch0Msgs[0].Bytes, wire.BinaryBytes(ch0Msg)) { 141 | t.Errorf("Unexpected message bytes. Wanted: %X, Got: %X", wire.BinaryBytes(ch0Msg), ch0Msgs[0].Bytes) 142 | } 143 | 144 | // Check message on ch1 145 | ch1Msgs := s2.Reactor("foo").(*TestReactor).getMsgs(byte(0x01)) 146 | if len(ch1Msgs) != 1 { 147 | t.Errorf("Expected to have received 1 message in ch1") 148 | } 149 | if !bytes.Equal(ch1Msgs[0].Bytes, wire.BinaryBytes(ch1Msg)) { 150 | t.Errorf("Unexpected message bytes. Wanted: %X, Got: %X", wire.BinaryBytes(ch1Msg), ch1Msgs[0].Bytes) 151 | } 152 | 153 | // Check message on ch2 154 | ch2Msgs := s2.Reactor("bar").(*TestReactor).getMsgs(byte(0x02)) 155 | if len(ch2Msgs) != 1 { 156 | t.Errorf("Expected to have received 1 message in ch2") 157 | } 158 | if !bytes.Equal(ch2Msgs[0].Bytes, wire.BinaryBytes(ch2Msg)) { 159 | t.Errorf("Unexpected message bytes. Wanted: %X, Got: %X", wire.BinaryBytes(ch2Msg), ch2Msgs[0].Bytes) 160 | } 161 | 162 | } 163 | 164 | func TestConnAddrFilter(t *testing.T) { 165 | s1 := makeSwitch(1, "testing", "123.123.123", initSwitchFunc) 166 | s2 := makeSwitch(1, "testing", "123.123.123", initSwitchFunc) 167 | 168 | c1, c2 := net.Pipe() 169 | 170 | s1.SetAddrFilter(func(addr net.Addr) error { 171 | if addr.String() == c1.RemoteAddr().String() { 172 | return fmt.Errorf("Error: pipe is blacklisted") 173 | } 174 | return nil 175 | }) 176 | 177 | // connect to good peer 178 | go func() { 179 | s1.addPeerWithConnection(c1) 180 | }() 181 | go func() { 182 | s2.addPeerWithConnection(c2) 183 | }() 184 | 185 | // Wait for things to happen, peers to get added... 186 | time.Sleep(100 * time.Millisecond * time.Duration(4)) 187 | 188 | defer s1.Stop() 189 | defer s2.Stop() 190 | if s1.Peers().Size() != 0 { 191 | t.Errorf("Expected s1 not to connect to peers, got %d", s1.Peers().Size()) 192 | } 193 | if s2.Peers().Size() != 0 { 194 | t.Errorf("Expected s2 not to connect to peers, got %d", s2.Peers().Size()) 195 | } 196 | } 197 | 198 | func TestConnPubKeyFilter(t *testing.T) { 199 | s1 := makeSwitch(1, "testing", "123.123.123", initSwitchFunc) 200 | s2 := makeSwitch(1, "testing", "123.123.123", initSwitchFunc) 201 | 202 | c1, c2 := net.Pipe() 203 | 204 | // set pubkey filter 205 | s1.SetPubKeyFilter(func(pubkey crypto.PubKeyEd25519) error { 206 | if bytes.Equal(pubkey.Bytes(), s2.nodeInfo.PubKey.Bytes()) { 207 | return fmt.Errorf("Error: pipe is blacklisted") 208 | } 209 | return nil 210 | }) 211 | 212 | // connect to good peer 213 | go func() { 214 | s1.addPeerWithConnection(c1) 215 | }() 216 | go func() { 217 | s2.addPeerWithConnection(c2) 218 | }() 219 | 220 | // Wait for things to happen, peers to get added... 221 | time.Sleep(100 * time.Millisecond * time.Duration(4)) 222 | 223 | defer s1.Stop() 224 | defer s2.Stop() 225 | if s1.Peers().Size() != 0 { 226 | t.Errorf("Expected s1 not to connect to peers, got %d", s1.Peers().Size()) 227 | } 228 | if s2.Peers().Size() != 0 { 229 | t.Errorf("Expected s2 not to connect to peers, got %d", s2.Peers().Size()) 230 | } 231 | } 232 | 233 | func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { 234 | assert, require := assert.New(t), require.New(t) 235 | 236 | sw := makeSwitch(1, "testing", "123.123.123", initSwitchFunc) 237 | sw.Start() 238 | defer sw.Stop() 239 | 240 | // simulate remote peer 241 | rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()} 242 | rp.Start() 243 | defer rp.Stop() 244 | 245 | peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey) 246 | require.Nil(err) 247 | err = sw.AddPeer(peer) 248 | require.Nil(err) 249 | 250 | // simulate failure by closing connection 251 | peer.CloseConn() 252 | 253 | time.Sleep(100 * time.Millisecond) 254 | 255 | assert.Zero(sw.Peers().Size()) 256 | assert.False(peer.IsRunning()) 257 | } 258 | 259 | func TestSwitchReconnectsToPersistentPeer(t *testing.T) { 260 | assert, require := assert.New(t), require.New(t) 261 | 262 | sw := makeSwitch(1, "testing", "123.123.123", initSwitchFunc) 263 | sw.Start() 264 | defer sw.Stop() 265 | 266 | // simulate remote peer 267 | rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()} 268 | rp.Start() 269 | defer rp.Stop() 270 | 271 | peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey) 272 | peer.makePersistent() 273 | require.Nil(err) 274 | err = sw.AddPeer(peer) 275 | require.Nil(err) 276 | 277 | // simulate failure by closing connection 278 | peer.CloseConn() 279 | 280 | time.Sleep(100 * time.Millisecond) 281 | 282 | assert.NotZero(sw.Peers().Size()) 283 | assert.False(peer.IsRunning()) 284 | } 285 | 286 | func BenchmarkSwitches(b *testing.B) { 287 | 288 | b.StopTimer() 289 | 290 | s1, s2 := makeSwitchPair(b, func(i int, sw *Switch) *Switch { 291 | // Make bar reactors of bar channels each 292 | sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{ 293 | &ChannelDescriptor{ID: byte(0x00), Priority: 10}, 294 | &ChannelDescriptor{ID: byte(0x01), Priority: 10}, 295 | }, false)) 296 | sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{ 297 | &ChannelDescriptor{ID: byte(0x02), Priority: 10}, 298 | &ChannelDescriptor{ID: byte(0x03), Priority: 10}, 299 | }, false)) 300 | return sw 301 | }) 302 | defer s1.Stop() 303 | defer s2.Stop() 304 | 305 | // Allow time for goroutines to boot up 306 | time.Sleep(1000 * time.Millisecond) 307 | b.StartTimer() 308 | 309 | numSuccess, numFailure := 0, 0 310 | 311 | // Send random message from foo channel to another 312 | for i := 0; i < b.N; i++ { 313 | chID := byte(i % 4) 314 | successChan := s1.Broadcast(chID, "test data") 315 | for s := range successChan { 316 | if s { 317 | numSuccess++ 318 | } else { 319 | numFailure++ 320 | } 321 | } 322 | } 323 | 324 | log.Warn(Fmt("success: %v, failure: %v", numSuccess, numFailure)) 325 | 326 | // Allow everything to flush before stopping switches & closing connections. 327 | b.StopTimer() 328 | time.Sleep(1000 * time.Millisecond) 329 | 330 | } 331 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/tendermint/go-crypto" 10 | ) 11 | 12 | const maxNodeInfoSize = 10240 // 10Kb 13 | 14 | type NodeInfo struct { 15 | PubKey crypto.PubKeyEd25519 `json:"pub_key"` 16 | Moniker string `json:"moniker"` 17 | Network string `json:"network"` 18 | RemoteAddr string `json:"remote_addr"` 19 | ListenAddr string `json:"listen_addr"` 20 | Version string `json:"version"` // major.minor.revision 21 | Other []string `json:"other"` // other application specific data 22 | } 23 | 24 | // CONTRACT: two nodes are compatible if the major/minor versions match and network match 25 | func (info *NodeInfo) CompatibleWith(other *NodeInfo) error { 26 | iMajor, iMinor, _, iErr := splitVersion(info.Version) 27 | oMajor, oMinor, _, oErr := splitVersion(other.Version) 28 | 29 | // if our own version number is not formatted right, we messed up 30 | if iErr != nil { 31 | return iErr 32 | } 33 | 34 | // version number must be formatted correctly ("x.x.x") 35 | if oErr != nil { 36 | return oErr 37 | } 38 | 39 | // major version must match 40 | if iMajor != oMajor { 41 | return fmt.Errorf("Peer is on a different major version. Got %v, expected %v", oMajor, iMajor) 42 | } 43 | 44 | // minor version must match 45 | if iMinor != oMinor { 46 | return fmt.Errorf("Peer is on a different minor version. Got %v, expected %v", oMinor, iMinor) 47 | } 48 | 49 | // nodes must be on the same network 50 | if info.Network != other.Network { 51 | return fmt.Errorf("Peer is on a different network. Got %v, expected %v", other.Network, info.Network) 52 | } 53 | 54 | return nil 55 | } 56 | 57 | func (info *NodeInfo) ListenHost() string { 58 | host, _, _ := net.SplitHostPort(info.ListenAddr) 59 | return host 60 | } 61 | 62 | func (info *NodeInfo) ListenPort() int { 63 | _, port, _ := net.SplitHostPort(info.ListenAddr) 64 | port_i, err := strconv.Atoi(port) 65 | if err != nil { 66 | return -1 67 | } 68 | return port_i 69 | } 70 | 71 | func splitVersion(version string) (string, string, string, error) { 72 | spl := strings.Split(version, ".") 73 | if len(spl) != 3 { 74 | return "", "", "", fmt.Errorf("Invalid version format %v", version) 75 | } 76 | return spl[0], spl[1], spl[2], nil 77 | } 78 | -------------------------------------------------------------------------------- /upnp/README.md: -------------------------------------------------------------------------------- 1 | # `tendermint/p2p/upnp` 2 | 3 | ## Resources 4 | 5 | * http://www.upnp-hacks.org/upnp.html 6 | -------------------------------------------------------------------------------- /upnp/log.go: -------------------------------------------------------------------------------- 1 | package upnp 2 | 3 | import ( 4 | "github.com/tendermint/go-logger" 5 | ) 6 | 7 | var log = logger.New("module", "upnp") 8 | -------------------------------------------------------------------------------- /upnp/probe.go: -------------------------------------------------------------------------------- 1 | package upnp 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | "time" 8 | 9 | . "github.com/tendermint/go-common" 10 | ) 11 | 12 | type UPNPCapabilities struct { 13 | PortMapping bool 14 | Hairpin bool 15 | } 16 | 17 | func makeUPNPListener(intPort int, extPort int) (NAT, net.Listener, net.IP, error) { 18 | nat, err := Discover() 19 | if err != nil { 20 | return nil, nil, nil, errors.New(fmt.Sprintf("NAT upnp could not be discovered: %v", err)) 21 | } 22 | log.Info(Fmt("ourIP: %v", nat.(*upnpNAT).ourIP)) 23 | 24 | ext, err := nat.GetExternalAddress() 25 | if err != nil { 26 | return nat, nil, nil, errors.New(fmt.Sprintf("External address error: %v", err)) 27 | } 28 | log.Info(Fmt("External address: %v", ext)) 29 | 30 | port, err := nat.AddPortMapping("tcp", extPort, intPort, "Tendermint UPnP Probe", 0) 31 | if err != nil { 32 | return nat, nil, ext, errors.New(fmt.Sprintf("Port mapping error: %v", err)) 33 | } 34 | log.Info(Fmt("Port mapping mapped: %v", port)) 35 | 36 | // also run the listener, open for all remote addresses. 37 | listener, err := net.Listen("tcp", fmt.Sprintf(":%v", intPort)) 38 | if err != nil { 39 | return nat, nil, ext, errors.New(fmt.Sprintf("Error establishing listener: %v", err)) 40 | } 41 | return nat, listener, ext, nil 42 | } 43 | 44 | func testHairpin(listener net.Listener, extAddr string) (supportsHairpin bool) { 45 | // Listener 46 | go func() { 47 | inConn, err := listener.Accept() 48 | if err != nil { 49 | log.Notice(Fmt("Listener.Accept() error: %v", err)) 50 | return 51 | } 52 | log.Info(Fmt("Accepted incoming connection: %v -> %v", inConn.LocalAddr(), inConn.RemoteAddr())) 53 | buf := make([]byte, 1024) 54 | n, err := inConn.Read(buf) 55 | if err != nil { 56 | log.Notice(Fmt("Incoming connection read error: %v", err)) 57 | return 58 | } 59 | log.Info(Fmt("Incoming connection read %v bytes: %X", n, buf)) 60 | if string(buf) == "test data" { 61 | supportsHairpin = true 62 | return 63 | } 64 | }() 65 | 66 | // Establish outgoing 67 | outConn, err := net.Dial("tcp", extAddr) 68 | if err != nil { 69 | log.Notice(Fmt("Outgoing connection dial error: %v", err)) 70 | return 71 | } 72 | 73 | n, err := outConn.Write([]byte("test data")) 74 | if err != nil { 75 | log.Notice(Fmt("Outgoing connection write error: %v", err)) 76 | return 77 | } 78 | log.Info(Fmt("Outgoing connection wrote %v bytes", n)) 79 | 80 | // Wait for data receipt 81 | time.Sleep(1 * time.Second) 82 | return 83 | } 84 | 85 | func Probe() (caps UPNPCapabilities, err error) { 86 | log.Info("Probing for UPnP!") 87 | 88 | intPort, extPort := 8001, 8001 89 | 90 | nat, listener, ext, err := makeUPNPListener(intPort, extPort) 91 | if err != nil { 92 | return 93 | } 94 | caps.PortMapping = true 95 | 96 | // Deferred cleanup 97 | defer func() { 98 | err = nat.DeletePortMapping("tcp", intPort, extPort) 99 | if err != nil { 100 | log.Warn(Fmt("Port mapping delete error: %v", err)) 101 | } 102 | listener.Close() 103 | }() 104 | 105 | supportsHairpin := testHairpin(listener, fmt.Sprintf("%v:%v", ext, extPort)) 106 | if supportsHairpin { 107 | caps.Hairpin = true 108 | } 109 | 110 | return 111 | } 112 | -------------------------------------------------------------------------------- /upnp/upnp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Taken from taipei-torrent 3 | 4 | Just enough UPnP to be able to forward ports 5 | */ 6 | package upnp 7 | 8 | // BUG(jae): TODO: use syscalls to get actual ourIP. http://pastebin.com/9exZG4rh 9 | 10 | import ( 11 | "bytes" 12 | "encoding/xml" 13 | "errors" 14 | "io/ioutil" 15 | "net" 16 | "net/http" 17 | "strconv" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | type upnpNAT struct { 23 | serviceURL string 24 | ourIP string 25 | urnDomain string 26 | } 27 | 28 | // protocol is either "udp" or "tcp" 29 | type NAT interface { 30 | GetExternalAddress() (addr net.IP, err error) 31 | AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) 32 | DeletePortMapping(protocol string, externalPort, internalPort int) (err error) 33 | } 34 | 35 | func Discover() (nat NAT, err error) { 36 | ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900") 37 | if err != nil { 38 | return 39 | } 40 | conn, err := net.ListenPacket("udp4", ":0") 41 | if err != nil { 42 | return 43 | } 44 | socket := conn.(*net.UDPConn) 45 | defer socket.Close() 46 | 47 | err = socket.SetDeadline(time.Now().Add(3 * time.Second)) 48 | if err != nil { 49 | return 50 | } 51 | 52 | st := "InternetGatewayDevice:1" 53 | 54 | buf := bytes.NewBufferString( 55 | "M-SEARCH * HTTP/1.1\r\n" + 56 | "HOST: 239.255.255.250:1900\r\n" + 57 | "ST: ssdp:all\r\n" + 58 | "MAN: \"ssdp:discover\"\r\n" + 59 | "MX: 2\r\n\r\n") 60 | message := buf.Bytes() 61 | answerBytes := make([]byte, 1024) 62 | for i := 0; i < 3; i++ { 63 | _, err = socket.WriteToUDP(message, ssdp) 64 | if err != nil { 65 | return 66 | } 67 | var n int 68 | n, _, err = socket.ReadFromUDP(answerBytes) 69 | for { 70 | n, _, err = socket.ReadFromUDP(answerBytes) 71 | if err != nil { 72 | break 73 | } 74 | answer := string(answerBytes[0:n]) 75 | if strings.Index(answer, st) < 0 { 76 | continue 77 | } 78 | // HTTP header field names are case-insensitive. 79 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 80 | locString := "\r\nlocation:" 81 | answer = strings.ToLower(answer) 82 | locIndex := strings.Index(answer, locString) 83 | if locIndex < 0 { 84 | continue 85 | } 86 | loc := answer[locIndex+len(locString):] 87 | endIndex := strings.Index(loc, "\r\n") 88 | if endIndex < 0 { 89 | continue 90 | } 91 | locURL := strings.TrimSpace(loc[0:endIndex]) 92 | var serviceURL, urnDomain string 93 | serviceURL, urnDomain, err = getServiceURL(locURL) 94 | if err != nil { 95 | return 96 | } 97 | var ourIP net.IP 98 | ourIP, err = localIPv4() 99 | if err != nil { 100 | return 101 | } 102 | nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain} 103 | return 104 | } 105 | } 106 | err = errors.New("UPnP port discovery failed.") 107 | return 108 | } 109 | 110 | type Envelope struct { 111 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` 112 | Soap *SoapBody 113 | } 114 | type SoapBody struct { 115 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` 116 | ExternalIP *ExternalIPAddressResponse 117 | } 118 | 119 | type ExternalIPAddressResponse struct { 120 | XMLName xml.Name `xml:"GetExternalIPAddressResponse"` 121 | IPAddress string `xml:"NewExternalIPAddress"` 122 | } 123 | 124 | type ExternalIPAddress struct { 125 | XMLName xml.Name `xml:"NewExternalIPAddress"` 126 | IP string 127 | } 128 | 129 | type UPNPService struct { 130 | ServiceType string `xml:"serviceType"` 131 | ControlURL string `xml:"controlURL"` 132 | } 133 | 134 | type DeviceList struct { 135 | Device []Device `xml:"device"` 136 | } 137 | 138 | type ServiceList struct { 139 | Service []UPNPService `xml:"service"` 140 | } 141 | 142 | type Device struct { 143 | XMLName xml.Name `xml:"device"` 144 | DeviceType string `xml:"deviceType"` 145 | DeviceList DeviceList `xml:"deviceList"` 146 | ServiceList ServiceList `xml:"serviceList"` 147 | } 148 | 149 | type Root struct { 150 | Device Device 151 | } 152 | 153 | func getChildDevice(d *Device, deviceType string) *Device { 154 | dl := d.DeviceList.Device 155 | for i := 0; i < len(dl); i++ { 156 | if strings.Index(dl[i].DeviceType, deviceType) >= 0 { 157 | return &dl[i] 158 | } 159 | } 160 | return nil 161 | } 162 | 163 | func getChildService(d *Device, serviceType string) *UPNPService { 164 | sl := d.ServiceList.Service 165 | for i := 0; i < len(sl); i++ { 166 | if strings.Index(sl[i].ServiceType, serviceType) >= 0 { 167 | return &sl[i] 168 | } 169 | } 170 | return nil 171 | } 172 | 173 | func localIPv4() (net.IP, error) { 174 | tt, err := net.Interfaces() 175 | if err != nil { 176 | return nil, err 177 | } 178 | for _, t := range tt { 179 | aa, err := t.Addrs() 180 | if err != nil { 181 | return nil, err 182 | } 183 | for _, a := range aa { 184 | ipnet, ok := a.(*net.IPNet) 185 | if !ok { 186 | continue 187 | } 188 | v4 := ipnet.IP.To4() 189 | if v4 == nil || v4[0] == 127 { // loopback address 190 | continue 191 | } 192 | return v4, nil 193 | } 194 | } 195 | return nil, errors.New("cannot find local IP address") 196 | } 197 | 198 | func getServiceURL(rootURL string) (url, urnDomain string, err error) { 199 | r, err := http.Get(rootURL) 200 | if err != nil { 201 | return 202 | } 203 | defer r.Body.Close() 204 | if r.StatusCode >= 400 { 205 | err = errors.New(string(r.StatusCode)) 206 | return 207 | } 208 | var root Root 209 | err = xml.NewDecoder(r.Body).Decode(&root) 210 | if err != nil { 211 | return 212 | } 213 | a := &root.Device 214 | if strings.Index(a.DeviceType, "InternetGatewayDevice:1") < 0 { 215 | err = errors.New("No InternetGatewayDevice") 216 | return 217 | } 218 | b := getChildDevice(a, "WANDevice:1") 219 | if b == nil { 220 | err = errors.New("No WANDevice") 221 | return 222 | } 223 | c := getChildDevice(b, "WANConnectionDevice:1") 224 | if c == nil { 225 | err = errors.New("No WANConnectionDevice") 226 | return 227 | } 228 | d := getChildService(c, "WANIPConnection:1") 229 | if d == nil { 230 | // Some routers don't follow the UPnP spec, and put WanIPConnection under WanDevice, 231 | // instead of under WanConnectionDevice 232 | d = getChildService(b, "WANIPConnection:1") 233 | 234 | if d == nil { 235 | err = errors.New("No WANIPConnection") 236 | return 237 | } 238 | } 239 | // Extract the domain name, which isn't always 'schemas-upnp-org' 240 | urnDomain = strings.Split(d.ServiceType, ":")[1] 241 | url = combineURL(rootURL, d.ControlURL) 242 | return 243 | } 244 | 245 | func combineURL(rootURL, subURL string) string { 246 | protocolEnd := "://" 247 | protoEndIndex := strings.Index(rootURL, protocolEnd) 248 | a := rootURL[protoEndIndex+len(protocolEnd):] 249 | rootIndex := strings.Index(a, "/") 250 | return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL 251 | } 252 | 253 | func soapRequest(url, function, message, domain string) (r *http.Response, err error) { 254 | fullMessage := "" + 255 | "\r\n" + 256 | "" + message + "" 257 | 258 | req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage)) 259 | if err != nil { 260 | return nil, err 261 | } 262 | req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"") 263 | req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3") 264 | //req.Header.Set("Transfer-Encoding", "chunked") 265 | req.Header.Set("SOAPAction", "\"urn:"+domain+":service:WANIPConnection:1#"+function+"\"") 266 | req.Header.Set("Connection", "Close") 267 | req.Header.Set("Cache-Control", "no-cache") 268 | req.Header.Set("Pragma", "no-cache") 269 | 270 | // log.Stderr("soapRequest ", req) 271 | 272 | r, err = http.DefaultClient.Do(req) 273 | if err != nil { 274 | return nil, err 275 | } 276 | /*if r.Body != nil { 277 | defer r.Body.Close() 278 | }*/ 279 | 280 | if r.StatusCode >= 400 { 281 | // log.Stderr(function, r.StatusCode) 282 | err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function) 283 | r = nil 284 | return 285 | } 286 | return 287 | } 288 | 289 | type statusInfo struct { 290 | externalIpAddress string 291 | } 292 | 293 | func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) { 294 | 295 | message := "\r\n" + 296 | "" 297 | 298 | var response *http.Response 299 | response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain) 300 | if response != nil { 301 | defer response.Body.Close() 302 | } 303 | if err != nil { 304 | return 305 | } 306 | var envelope Envelope 307 | data, err := ioutil.ReadAll(response.Body) 308 | reader := bytes.NewReader(data) 309 | xml.NewDecoder(reader).Decode(&envelope) 310 | 311 | info = statusInfo{envelope.Soap.ExternalIP.IPAddress} 312 | 313 | if err != nil { 314 | return 315 | } 316 | 317 | return 318 | } 319 | 320 | func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) { 321 | info, err := n.getExternalIPAddress() 322 | if err != nil { 323 | return 324 | } 325 | addr = net.ParseIP(info.externalIpAddress) 326 | return 327 | } 328 | 329 | func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) { 330 | // A single concatenation would break ARM compilation. 331 | message := "\r\n" + 332 | "" + strconv.Itoa(externalPort) 333 | message += "" + protocol + "" 334 | message += "" + strconv.Itoa(internalPort) + "" + 335 | "" + n.ourIP + "" + 336 | "1" 337 | message += description + 338 | "" + strconv.Itoa(timeout) + 339 | "" 340 | 341 | var response *http.Response 342 | response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain) 343 | if response != nil { 344 | defer response.Body.Close() 345 | } 346 | if err != nil { 347 | return 348 | } 349 | 350 | // TODO: check response to see if the port was forwarded 351 | // log.Println(message, response) 352 | // JAE: 353 | // body, err := ioutil.ReadAll(response.Body) 354 | // fmt.Println(string(body), err) 355 | mappedExternalPort = externalPort 356 | _ = response 357 | return 358 | } 359 | 360 | func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) { 361 | 362 | message := "\r\n" + 363 | "" + strconv.Itoa(externalPort) + 364 | "" + protocol + "" + 365 | "" 366 | 367 | var response *http.Response 368 | response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain) 369 | if response != nil { 370 | defer response.Body.Close() 371 | } 372 | if err != nil { 373 | return 374 | } 375 | 376 | // TODO: check response to see if the port was deleted 377 | // log.Println(message, response) 378 | _ = response 379 | return 380 | } 381 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "crypto/sha256" 5 | ) 6 | 7 | // doubleSha256 calculates sha256(sha256(b)) and returns the resulting bytes. 8 | func doubleSha256(b []byte) []byte { 9 | hasher := sha256.New() 10 | hasher.Write(b) 11 | sum := hasher.Sum(nil) 12 | hasher.Reset() 13 | hasher.Write(sum) 14 | return hasher.Sum(nil) 15 | } 16 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | const Version = "0.5.0" 4 | --------------------------------------------------------------------------------