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  }