github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/protocol/identify/id.go (about) 1 package identify 2 3 import ( 4 "strings" 5 "sync" 6 7 semver "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/coreos/go-semver/semver" 8 ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" 9 ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" 10 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 11 12 mstream "github.com/ipfs/go-ipfs/metrics/stream" 13 host "github.com/ipfs/go-ipfs/p2p/host" 14 inet "github.com/ipfs/go-ipfs/p2p/net" 15 peer "github.com/ipfs/go-ipfs/p2p/peer" 16 protocol "github.com/ipfs/go-ipfs/p2p/protocol" 17 pb "github.com/ipfs/go-ipfs/p2p/protocol/identify/pb" 18 config "github.com/ipfs/go-ipfs/repo/config" 19 eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" 20 lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" 21 ) 22 23 var log = eventlog.Logger("net/identify") 24 25 // ID is the protocol.ID of the Identify Service. 26 const ID protocol.ID = "/ipfs/identify" 27 28 // IpfsVersion holds the current protocol version for a client running this code 29 // TODO(jbenet): fix the versioning mess. 30 const IpfsVersion = "ipfs/0.1.0" 31 const ClientVersion = "go-ipfs/" + config.CurrentVersionNumber 32 33 // IDService is a structure that implements ProtocolIdentify. 34 // It is a trivial service that gives the other peer some 35 // useful information about the local peer. A sort of hello. 36 // 37 // The IDService sends: 38 // * Our IPFS Protocol Version 39 // * Our IPFS Agent Version 40 // * Our public Listen Addresses 41 type IDService struct { 42 Host host.Host 43 44 // connections undergoing identification 45 // for wait purposes 46 currid map[inet.Conn]chan struct{} 47 currmu sync.RWMutex 48 49 // our own observed addresses. 50 // TODO: instead of expiring, remove these when we disconnect 51 observedAddrs ObservedAddrSet 52 } 53 54 func NewIDService(h host.Host) *IDService { 55 s := &IDService{ 56 Host: h, 57 currid: make(map[inet.Conn]chan struct{}), 58 } 59 h.SetStreamHandler(ID, s.RequestHandler) 60 return s 61 } 62 63 // OwnObservedAddrs returns the addresses peers have reported we've dialed from 64 func (ids *IDService) OwnObservedAddrs() []ma.Multiaddr { 65 return ids.observedAddrs.Addrs() 66 } 67 68 func (ids *IDService) IdentifyConn(c inet.Conn) { 69 ids.currmu.Lock() 70 if wait, found := ids.currid[c]; found { 71 ids.currmu.Unlock() 72 log.Debugf("IdentifyConn called twice on: %s", c) 73 <-wait // already identifying it. wait for it. 74 return 75 } 76 ids.currid[c] = make(chan struct{}) 77 ids.currmu.Unlock() 78 79 s, err := c.NewStream() 80 if err != nil { 81 log.Debugf("error opening initial stream for %s", ID) 82 log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer()) 83 c.Close() 84 return 85 } else { 86 bwc := ids.Host.GetBandwidthReporter() 87 s = mstream.WrapStream(s, ID, bwc) 88 89 // ok give the response to our handler. 90 if err := protocol.WriteHeader(s, ID); err != nil { 91 log.Debugf("error writing stream header for %s", ID) 92 log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer()) 93 s.Close() 94 c.Close() 95 return 96 } 97 ids.ResponseHandler(s) 98 } 99 100 ids.currmu.Lock() 101 ch, found := ids.currid[c] 102 delete(ids.currid, c) 103 ids.currmu.Unlock() 104 105 if !found { 106 log.Debugf("IdentifyConn failed to find channel (programmer error) for %s", c) 107 return 108 } 109 110 close(ch) // release everyone waiting. 111 } 112 113 func (ids *IDService) RequestHandler(s inet.Stream) { 114 defer s.Close() 115 c := s.Conn() 116 117 bwc := ids.Host.GetBandwidthReporter() 118 s = mstream.WrapStream(s, ID, bwc) 119 120 w := ggio.NewDelimitedWriter(s) 121 mes := pb.Identify{} 122 ids.populateMessage(&mes, s.Conn()) 123 w.WriteMsg(&mes) 124 125 log.Debugf("%s sent message to %s %s", ID, 126 c.RemotePeer(), c.RemoteMultiaddr()) 127 } 128 129 func (ids *IDService) ResponseHandler(s inet.Stream) { 130 defer s.Close() 131 c := s.Conn() 132 133 r := ggio.NewDelimitedReader(s, 2048) 134 mes := pb.Identify{} 135 if err := r.ReadMsg(&mes); err != nil { 136 return 137 } 138 ids.consumeMessage(&mes, c) 139 140 log.Debugf("%s received message from %s %s", ID, 141 c.RemotePeer(), c.RemoteMultiaddr()) 142 } 143 144 func (ids *IDService) populateMessage(mes *pb.Identify, c inet.Conn) { 145 146 // set protocols this node is currently handling 147 protos := ids.Host.Mux().Protocols() 148 mes.Protocols = make([]string, len(protos)) 149 for i, p := range protos { 150 mes.Protocols[i] = string(p) 151 } 152 153 // observed address so other side is informed of their 154 // "public" address, at least in relation to us. 155 mes.ObservedAddr = c.RemoteMultiaddr().Bytes() 156 157 // set listen addrs, get our latest addrs from Host. 158 laddrs := ids.Host.Addrs() 159 mes.ListenAddrs = make([][]byte, len(laddrs)) 160 for i, addr := range laddrs { 161 mes.ListenAddrs[i] = addr.Bytes() 162 } 163 log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs) 164 165 // set protocol versions 166 pv := IpfsVersion 167 av := ClientVersion 168 mes.ProtocolVersion = &pv 169 mes.AgentVersion = &av 170 } 171 172 func (ids *IDService) consumeMessage(mes *pb.Identify, c inet.Conn) { 173 p := c.RemotePeer() 174 175 // mes.Protocols 176 177 // mes.ObservedAddr 178 ids.consumeObservedAddress(mes.GetObservedAddr(), c) 179 180 // mes.ListenAddrs 181 laddrs := mes.GetListenAddrs() 182 lmaddrs := make([]ma.Multiaddr, 0, len(laddrs)) 183 for _, addr := range laddrs { 184 maddr, err := ma.NewMultiaddrBytes(addr) 185 if err != nil { 186 log.Debugf("%s failed to parse multiaddr from %s %s", ID, 187 p, c.RemoteMultiaddr()) 188 continue 189 } 190 lmaddrs = append(lmaddrs, maddr) 191 } 192 193 // update our peerstore with the addresses. here, we SET the addresses, clearing old ones. 194 // We are receiving from the peer itself. this is current address ground truth. 195 ids.Host.Peerstore().SetAddrs(p, lmaddrs, peer.ConnectedAddrTTL) 196 log.Debugf("%s received listen addrs for %s: %s", c.LocalPeer(), c.RemotePeer(), lmaddrs) 197 198 // get protocol versions 199 pv := mes.GetProtocolVersion() 200 av := mes.GetAgentVersion() 201 202 // version check. if we shouldn't talk, bail. 203 // TODO: at this point, we've already exchanged information. 204 // move this into a first handshake before the connection can open streams. 205 if !protocolVersionsAreCompatible(pv, IpfsVersion) { 206 logProtocolMismatchDisconnect(c, pv, av) 207 c.Close() 208 return 209 } 210 211 ids.Host.Peerstore().Put(p, "ProtocolVersion", pv) 212 ids.Host.Peerstore().Put(p, "AgentVersion", av) 213 } 214 215 // IdentifyWait returns a channel which will be closed once 216 // "ProtocolIdentify" (handshake3) finishes on given conn. 217 // This happens async so the connection can start to be used 218 // even if handshake3 knowledge is not necesary. 219 // Users **MUST** call IdentifyWait _after_ IdentifyConn 220 func (ids *IDService) IdentifyWait(c inet.Conn) <-chan struct{} { 221 ids.currmu.Lock() 222 ch, found := ids.currid[c] 223 ids.currmu.Unlock() 224 if found { 225 return ch 226 } 227 228 // if not found, it means we are already done identifying it, or 229 // haven't even started. either way, return a new channel closed. 230 ch = make(chan struct{}) 231 close(ch) 232 return ch 233 } 234 235 func (ids *IDService) consumeObservedAddress(observed []byte, c inet.Conn) { 236 if observed == nil { 237 return 238 } 239 240 maddr, err := ma.NewMultiaddrBytes(observed) 241 if err != nil { 242 log.Debugf("error parsing received observed addr for %s: %s", c, err) 243 return 244 } 245 246 // we should only use ObservedAddr when our connection's LocalAddr is one 247 // of our ListenAddrs. If we Dial out using an ephemeral addr, knowing that 248 // address's external mapping is not very useful because the port will not be 249 // the same as the listen addr. 250 ifaceaddrs, err := ids.Host.Network().InterfaceListenAddresses() 251 if err != nil { 252 log.Infof("failed to get interface listen addrs", err) 253 return 254 } 255 256 log.Debugf("identify identifying observed multiaddr: %s %s", c.LocalMultiaddr(), ifaceaddrs) 257 if !addrInAddrs(c.LocalMultiaddr(), ifaceaddrs) { 258 // not in our list 259 return 260 } 261 262 // ok! we have the observed version of one of our ListenAddresses! 263 log.Debugf("added own observed listen addr: %s --> %s", c.LocalMultiaddr(), maddr) 264 ids.observedAddrs.Add(maddr, c.RemoteMultiaddr()) 265 } 266 267 func addrInAddrs(a ma.Multiaddr, as []ma.Multiaddr) bool { 268 for _, b := range as { 269 if a.Equal(b) { 270 return true 271 } 272 } 273 return false 274 } 275 276 // protocolVersionsAreCompatible checks that the two implementations 277 // can talk to each other. It will use semver, but for now while 278 // we're in tight development, we will return false for minor version 279 // changes too. 280 func protocolVersionsAreCompatible(v1, v2 string) bool { 281 if strings.HasPrefix(v1, "ipfs/") { 282 v1 = v1[5:] 283 } 284 if strings.HasPrefix(v2, "ipfs/") { 285 v2 = v2[5:] 286 } 287 288 v1s, err := semver.NewVersion(v1) 289 if err != nil { 290 return false 291 } 292 293 v2s, err := semver.NewVersion(v2) 294 if err != nil { 295 return false 296 } 297 298 return v1s.Major == v2s.Major && v1s.Minor == v2s.Minor 299 } 300 301 // netNotifiee defines methods to be used with the IpfsDHT 302 type netNotifiee IDService 303 304 func (nn *netNotifiee) IDService() *IDService { 305 return (*IDService)(nn) 306 } 307 308 func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) { 309 // TODO: deprecate the setConnHandler hook, and kick off 310 // identification here. 311 } 312 313 func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) { 314 // undo the setting of addresses to peer.ConnectedAddrTTL we did 315 ids := nn.IDService() 316 ps := ids.Host.Peerstore() 317 addrs := ps.Addrs(v.RemotePeer()) 318 ps.SetAddrs(v.RemotePeer(), addrs, peer.RecentlyConnectedAddrTTL) 319 } 320 321 func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {} 322 func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {} 323 func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr) {} 324 func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {} 325 326 func logProtocolMismatchDisconnect(c inet.Conn, protocol, agent string) { 327 lm := make(lgbl.DeferredMap) 328 lm["remotePeer"] = func() interface{} { return c.RemotePeer().Pretty() } 329 lm["remoteAddr"] = func() interface{} { return c.RemoteMultiaddr().String() } 330 lm["protocolVersion"] = protocol 331 lm["agentVersion"] = agent 332 log.Event(context.TODO(), "IdentifyProtocolMismatch", lm) 333 log.Debug("IdentifyProtocolMismatch %s %s %s (disconnected)", c.RemotePeer(), protocol, agent) 334 }