github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/libp2p/internal/handshake/handshake.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package handshake 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "sync/atomic" 12 "time" 13 14 "github.com/ethersphere/bee/v2/pkg/bzz" 15 "github.com/ethersphere/bee/v2/pkg/crypto" 16 "github.com/ethersphere/bee/v2/pkg/log" 17 "github.com/ethersphere/bee/v2/pkg/p2p" 18 "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/handshake/pb" 19 "github.com/ethersphere/bee/v2/pkg/p2p/protobuf" 20 "github.com/ethersphere/bee/v2/pkg/swarm" 21 22 libp2ppeer "github.com/libp2p/go-libp2p/core/peer" 23 ma "github.com/multiformats/go-multiaddr" 24 ) 25 26 // loggerName is the tree path name of the logger for this package. 27 const loggerName = "handshake" 28 29 const ( 30 // ProtocolName is the text of the name of the handshake protocol. 31 ProtocolName = "handshake" 32 // ProtocolVersion is the current handshake protocol version. 33 ProtocolVersion = "12.0.0" 34 // StreamName is the name of the stream used for handshake purposes. 35 StreamName = "handshake" 36 // MaxWelcomeMessageLength is maximum number of characters allowed in the welcome message. 37 MaxWelcomeMessageLength = 140 38 handshakeTimeout = 15 * time.Second 39 ) 40 41 var ( 42 // ErrNetworkIDIncompatible is returned if response from the other peer does not have valid networkID. 43 ErrNetworkIDIncompatible = errors.New("incompatible network ID") 44 45 // ErrInvalidAck is returned if data in received in ack is not valid (invalid signature for example). 46 ErrInvalidAck = errors.New("invalid ack") 47 48 // ErrInvalidSyn is returned if observable address in ack is not a valid.. 49 ErrInvalidSyn = errors.New("invalid syn") 50 51 // ErrWelcomeMessageLength is returned if the welcome message is longer than the maximum length 52 ErrWelcomeMessageLength = fmt.Errorf("handshake welcome message longer than maximum of %d characters", MaxWelcomeMessageLength) 53 54 // ErrPicker is returned if the picker (kademlia) rejects the peer 55 ErrPicker = errors.New("picker rejection") 56 ) 57 58 // AdvertisableAddressResolver can Resolve a Multiaddress. 59 type AdvertisableAddressResolver interface { 60 Resolve(observedAddress ma.Multiaddr) (ma.Multiaddr, error) 61 } 62 63 // Service can perform initiate or handle a handshake between peers. 64 type Service struct { 65 signer crypto.Signer 66 advertisableAddresser AdvertisableAddressResolver 67 overlay swarm.Address 68 fullNode bool 69 nonce []byte 70 networkID uint64 71 validateOverlay bool 72 welcomeMessage atomic.Value 73 logger log.Logger 74 libp2pID libp2ppeer.ID 75 metrics metrics 76 picker p2p.Picker 77 } 78 79 // Info contains the information received from the handshake. 80 type Info struct { 81 BzzAddress *bzz.Address 82 FullNode bool 83 } 84 85 func (i *Info) LightString() string { 86 if !i.FullNode { 87 return " (light)" 88 } 89 90 return "" 91 } 92 93 // New creates a new handshake Service. 94 func New(signer crypto.Signer, advertisableAddresser AdvertisableAddressResolver, overlay swarm.Address, networkID uint64, fullNode bool, nonce []byte, welcomeMessage string, validateOverlay bool, ownPeerID libp2ppeer.ID, logger log.Logger) (*Service, error) { 95 if len(welcomeMessage) > MaxWelcomeMessageLength { 96 return nil, ErrWelcomeMessageLength 97 } 98 99 svc := &Service{ 100 signer: signer, 101 advertisableAddresser: advertisableAddresser, 102 overlay: overlay, 103 networkID: networkID, 104 fullNode: fullNode, 105 validateOverlay: validateOverlay, 106 nonce: nonce, 107 libp2pID: ownPeerID, 108 logger: logger.WithName(loggerName).Register(), 109 metrics: newMetrics(), 110 } 111 svc.welcomeMessage.Store(welcomeMessage) 112 113 return svc, nil 114 } 115 116 func (s *Service) SetPicker(n p2p.Picker) { 117 s.picker = n 118 } 119 120 // Handshake initiates a handshake with a peer. 121 func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiaddr ma.Multiaddr, peerID libp2ppeer.ID) (i *Info, err error) { 122 loggerV1 := s.logger.V(1).Register() 123 124 ctx, cancel := context.WithTimeout(ctx, handshakeTimeout) 125 defer cancel() 126 127 w, r := protobuf.NewWriterAndReader(stream) 128 fullRemoteMA, err := buildFullMA(peerMultiaddr, peerID) 129 if err != nil { 130 return nil, err 131 } 132 133 fullRemoteMABytes, err := fullRemoteMA.MarshalBinary() 134 if err != nil { 135 return nil, err 136 } 137 138 if err := w.WriteMsgWithContext(ctx, &pb.Syn{ 139 ObservedUnderlay: fullRemoteMABytes, 140 }); err != nil { 141 return nil, fmt.Errorf("write syn message: %w", err) 142 } 143 144 var resp pb.SynAck 145 if err := r.ReadMsgWithContext(ctx, &resp); err != nil { 146 return nil, fmt.Errorf("read synack message: %w", err) 147 } 148 149 observedUnderlay, err := ma.NewMultiaddrBytes(resp.Syn.ObservedUnderlay) 150 if err != nil { 151 return nil, ErrInvalidSyn 152 } 153 154 observedUnderlayAddrInfo, err := libp2ppeer.AddrInfoFromP2pAddr(observedUnderlay) 155 if err != nil { 156 return nil, fmt.Errorf("extract addr from P2P: %w", err) 157 } 158 159 if s.libp2pID != observedUnderlayAddrInfo.ID { 160 // NOTE eventually we will return error here, but for now we want to gather some statistics 161 s.logger.Warning("received peer ID does not match ours", "their", observedUnderlayAddrInfo.ID, "ours", s.libp2pID) 162 } 163 164 advertisableUnderlay, err := s.advertisableAddresser.Resolve(observedUnderlay) 165 if err != nil { 166 return nil, err 167 } 168 169 bzzAddress, err := bzz.NewAddress(s.signer, advertisableUnderlay, s.overlay, s.networkID, s.nonce) 170 if err != nil { 171 return nil, err 172 } 173 174 advertisableUnderlayBytes, err := bzzAddress.Underlay.MarshalBinary() 175 if err != nil { 176 return nil, err 177 } 178 179 if resp.Ack.NetworkID != s.networkID { 180 return nil, ErrNetworkIDIncompatible 181 } 182 183 remoteBzzAddress, err := s.parseCheckAck(resp.Ack) 184 if err != nil { 185 return nil, err 186 } 187 188 // Synced read: 189 welcomeMessage := s.GetWelcomeMessage() 190 msg := &pb.Ack{ 191 Address: &pb.BzzAddress{ 192 Underlay: advertisableUnderlayBytes, 193 Overlay: bzzAddress.Overlay.Bytes(), 194 Signature: bzzAddress.Signature, 195 }, 196 NetworkID: s.networkID, 197 FullNode: s.fullNode, 198 Nonce: s.nonce, 199 WelcomeMessage: welcomeMessage, 200 } 201 202 if err := w.WriteMsgWithContext(ctx, msg); err != nil { 203 return nil, fmt.Errorf("write ack message: %w", err) 204 } 205 206 loggerV1.Debug("handshake finished for peer (outbound)", "peer_address", remoteBzzAddress.Overlay) 207 if len(resp.Ack.WelcomeMessage) > 0 { 208 s.logger.Debug("greeting message from peer", "peer_address", remoteBzzAddress.Overlay, "message", resp.Ack.WelcomeMessage) 209 } 210 211 return &Info{ 212 BzzAddress: remoteBzzAddress, 213 FullNode: resp.Ack.FullNode, 214 }, nil 215 } 216 217 // Handle handles an incoming handshake from a peer. 218 func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr ma.Multiaddr, remotePeerID libp2ppeer.ID) (i *Info, err error) { 219 loggerV1 := s.logger.V(1).Register() 220 221 ctx, cancel := context.WithTimeout(ctx, handshakeTimeout) 222 defer cancel() 223 224 w, r := protobuf.NewWriterAndReader(stream) 225 fullRemoteMA, err := buildFullMA(remoteMultiaddr, remotePeerID) 226 if err != nil { 227 return nil, err 228 } 229 230 fullRemoteMABytes, err := fullRemoteMA.MarshalBinary() 231 if err != nil { 232 return nil, err 233 } 234 235 var syn pb.Syn 236 if err := r.ReadMsgWithContext(ctx, &syn); err != nil { 237 s.metrics.SynRxFailed.Inc() 238 return nil, fmt.Errorf("read syn message: %w", err) 239 } 240 s.metrics.SynRx.Inc() 241 242 observedUnderlay, err := ma.NewMultiaddrBytes(syn.ObservedUnderlay) 243 if err != nil { 244 return nil, ErrInvalidSyn 245 } 246 247 advertisableUnderlay, err := s.advertisableAddresser.Resolve(observedUnderlay) 248 if err != nil { 249 return nil, err 250 } 251 252 bzzAddress, err := bzz.NewAddress(s.signer, advertisableUnderlay, s.overlay, s.networkID, s.nonce) 253 if err != nil { 254 return nil, err 255 } 256 257 advertisableUnderlayBytes, err := bzzAddress.Underlay.MarshalBinary() 258 if err != nil { 259 return nil, err 260 } 261 262 welcomeMessage := s.GetWelcomeMessage() 263 264 if err := w.WriteMsgWithContext(ctx, &pb.SynAck{ 265 Syn: &pb.Syn{ 266 ObservedUnderlay: fullRemoteMABytes, 267 }, 268 Ack: &pb.Ack{ 269 Address: &pb.BzzAddress{ 270 Underlay: advertisableUnderlayBytes, 271 Overlay: bzzAddress.Overlay.Bytes(), 272 Signature: bzzAddress.Signature, 273 }, 274 NetworkID: s.networkID, 275 FullNode: s.fullNode, 276 Nonce: s.nonce, 277 WelcomeMessage: welcomeMessage, 278 }, 279 }); err != nil { 280 s.metrics.SynAckTxFailed.Inc() 281 return nil, fmt.Errorf("write synack message: %w", err) 282 } 283 s.metrics.SynAckTx.Inc() 284 285 var ack pb.Ack 286 if err := r.ReadMsgWithContext(ctx, &ack); err != nil { 287 s.metrics.AckRxFailed.Inc() 288 return nil, fmt.Errorf("read ack message: %w", err) 289 } 290 s.metrics.AckRx.Inc() 291 292 if ack.NetworkID != s.networkID { 293 return nil, ErrNetworkIDIncompatible 294 } 295 296 overlay := swarm.NewAddress(ack.Address.Overlay) 297 298 if s.picker != nil { 299 if !s.picker.Pick(p2p.Peer{Address: overlay, FullNode: ack.FullNode}) { 300 return nil, ErrPicker 301 } 302 } 303 304 remoteBzzAddress, err := s.parseCheckAck(&ack) 305 if err != nil { 306 return nil, err 307 } 308 309 loggerV1.Debug("handshake finished for peer (inbound)", "peer_address", remoteBzzAddress.Overlay) 310 if len(ack.WelcomeMessage) > 0 { 311 loggerV1.Debug("greeting message from peer", "peer_address", remoteBzzAddress.Overlay, "message", ack.WelcomeMessage) 312 } 313 314 return &Info{ 315 BzzAddress: remoteBzzAddress, 316 FullNode: ack.FullNode, 317 }, nil 318 } 319 320 // SetWelcomeMessage sets the new handshake welcome message. 321 func (s *Service) SetWelcomeMessage(msg string) (err error) { 322 if len(msg) > MaxWelcomeMessageLength { 323 return ErrWelcomeMessageLength 324 } 325 s.welcomeMessage.Store(msg) 326 return nil 327 } 328 329 // GetWelcomeMessage returns the current handshake welcome message. 330 func (s *Service) GetWelcomeMessage() string { 331 return s.welcomeMessage.Load().(string) 332 } 333 334 func buildFullMA(addr ma.Multiaddr, peerID libp2ppeer.ID) (ma.Multiaddr, error) { 335 return ma.NewMultiaddr(fmt.Sprintf("%s/p2p/%s", addr.String(), peerID.String())) 336 } 337 338 func (s *Service) parseCheckAck(ack *pb.Ack) (*bzz.Address, error) { 339 bzzAddress, err := bzz.ParseAddress(ack.Address.Underlay, ack.Address.Overlay, ack.Address.Signature, ack.Nonce, s.validateOverlay, s.networkID) 340 if err != nil { 341 return nil, ErrInvalidAck 342 } 343 344 return bzzAddress, nil 345 }