func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*PBDHTMessage, error) { pmes := Message{ Type: PBDHTMessage_FIND_NODE, Key: string(id), ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, } mes := swarm.NewMessage(p, pmes.ToProtobuf()) listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) t := time.Now() dht.netChan.Outgoing <- mes after := time.After(timeout) select { case <-after: dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp := <-listenChan: roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } return pmesOut, nil } }
func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*PBDHTMessage, error) { pmes := Message{ Type: PBDHTMessage_GET_PROVIDERS, Key: string(key), ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, } mes := swarm.NewMessage(p, pmes.ToProtobuf()) listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) dht.netChan.Outgoing <- mes after := time.After(timeout) select { case <-after: dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp := <-listenChan: u.DOut("FindProviders: got response.\n") pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } return pmesOut, nil } }
func (dr *DagReader) precalcNextBuf() error { if dr.position >= len(dr.node.Links) { return io.EOF } nxtLink := dr.node.Links[dr.position] nxt := nxtLink.Node if nxt == nil { nxtNode, err := dr.serv.Get(u.Key(nxtLink.Hash)) if err != nil { return err } nxt = nxtNode } pb := new(PBData) err := proto.Unmarshal(nxt.Data, pb) if err != nil { return err } dr.position++ switch pb.GetType() { case PBData_Directory: panic("Why is there a directory under a file?") case PBData_File: //TODO: this *should* work, needs testing first //return NewDagReader(nxt, dr.serv) panic("Not yet handling different layers of indirection!") case PBData_Raw: dr.buf = bytes.NewBuffer(pb.GetData()) return nil default: panic("Unrecognized node type!") } }
// getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) { pmes := Message{ Type: PBDHTMessage_GET_VALUE, Key: string(key), Value: []byte{byte(level)}, ID: swarm.GenerateMessageID(), } responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) mes := swarm.NewMessage(p, pmes.ToProtobuf()) t := time.Now() dht.netChan.Outgoing <- mes // Wait for either the response or a timeout timeup := time.After(timeout) select { case <-timeup: dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp, ok := <-responseChan: if !ok { u.PErr("response channel closed before timeout, please investigate.\n") return nil, u.ErrTimeout } roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } return pmesOut, nil } }
func (bs *BitSwap) handleMessages() { for { select { case mes := <-bs.meschan.Incoming: pmes := new(PBMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { u.PErr("%v\n", err) continue } if pmes.Blocks != nil { for _, blkData := range pmes.Blocks { blk, err := blocks.NewBlock(blkData) if err != nil { u.PErr("%v\n", err) continue } go bs.blockReceive(mes.Peer, blk) } } if pmes.Wantlist != nil { for _, want := range pmes.Wantlist { go bs.peerWantsBlock(mes.Peer, want) } } case <-bs.haltChan: return } } }
// Read in all messages from swarm and handle them appropriately // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { u.DOut("Begin message handling routine\n") errs := dht.network.GetErrChan() for { select { case mes, ok := <-dht.netChan.Incoming: if !ok { u.DOut("handleMessages closing, bad recv on incoming\n") return } pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { u.PErr("Failed to decode protobuf message: %s\n", err) continue } dht.Update(mes.Peer) // Note: not sure if this is the correct place for this if pmes.GetResponse() { dht.listener.Respond(pmes.GetId(), mes) continue } // u.DOut("[peer: %s]\nGot message type: '%s' [id = %x, from = %s]\n", dht.self.ID.Pretty(), PBDHTMessage_MessageType_name[int32(pmes.GetType())], pmes.GetId(), mes.Peer.ID.Pretty()) switch pmes.GetType() { case PBDHTMessage_GET_VALUE: go dht.handleGetValue(mes.Peer, pmes) case PBDHTMessage_PUT_VALUE: go dht.handlePutValue(mes.Peer, pmes) case PBDHTMessage_FIND_NODE: go dht.handleFindPeer(mes.Peer, pmes) case PBDHTMessage_ADD_PROVIDER: go dht.handleAddProvider(mes.Peer, pmes) case PBDHTMessage_GET_PROVIDERS: go dht.handleGetProviders(mes.Peer, pmes) case PBDHTMessage_PING: go dht.handlePing(mes.Peer, pmes) case PBDHTMessage_DIAGNOSTIC: go dht.handleDiagnostic(mes.Peer, pmes) default: u.PErr("Recieved invalid message type") } case err := <-errs: u.PErr("dht err: %s\n", err) case <-dht.shutdown: return } } }
// If less than K nodes are in the entire network, it should fail when we make // a GET rpc and nobody has the value func TestLessThanKResponses(t *testing.T) { u.Debug = false fn := newFauxNet() fn.Listen() local := new(peer.Peer) local.ID = peer.ID("test_peer") d := NewDHT(local, fn, ds.NewMapDatastore()) d.Start() var ps []*peer.Peer for i := 0; i < 5; i++ { ps = append(ps, _randPeer()) d.Update(ps[i]) } other := _randPeer() // Reply with random peers to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { t.Fatal(err) } switch pmes.GetType() { case PBDHTMessage_GET_VALUE: resp := Message{ Type: pmes.GetType(), ID: pmes.GetId(), Response: true, Success: false, Peers: []*peer.Peer{other}, } return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) default: panic("Shouldnt recieve this.") } }) _, err := d.GetValue(u.Key("hello"), time.Second*30) if err != nil { switch err { case u.ErrNotFound: //Success! return case u.ErrTimeout: t.Fatal("Should not have gotten timeout!") default: t.Fatalf("Got unexpected error: %s", err) } } t.Fatal("Expected to recieve an error.") }
func Unwrap(data []byte) (*PBWrapper, error) { mes := new(PBWrapper) err := proto.Unmarshal(data, mes) if err != nil { return nil, err } return mes, nil }
func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { u.DOut("Begin Diagnostic") //Send to N closest peers targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) // TODO: Add timeout to this struct so nodes know when to return pmes := Message{ Type: PBDHTMessage_DIAGNOSTIC, ID: swarm.GenerateMessageID(), } listenChan := dht.listener.Listen(pmes.ID, len(targets), time.Minute*2) pbmes := pmes.ToProtobuf() for _, p := range targets { mes := swarm.NewMessage(p, pbmes) dht.netChan.Outgoing <- mes } var out []*diagInfo after := time.After(timeout) for count := len(targets); count > 0; { select { case <-after: u.DOut("Diagnostic request timed out.") return out, u.ErrTimeout case resp := <-listenChan: pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { // NOTE: here and elsewhere, need to audit error handling, // some errors should be continued on from return out, err } dec := json.NewDecoder(bytes.NewBuffer(pmesOut.GetValue())) for { di := new(diagInfo) err := dec.Decode(di) if err != nil { break } out = append(out, di) } } } return nil, nil }
func UnmarshalPrivateKey(data []byte) (PrivKey, error) { pmes := new(PBPrivateKey) err := proto.Unmarshal(data, pmes) if err != nil { return nil, err } switch pmes.GetType() { case KeyType_RSA: return UnmarshalRsaPrivateKey(pmes.GetData()) default: return nil, ErrBadKeyType } }
// NOTE: not yet finished, low priority func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30) for _, ps := range seq { mes := swarm.NewMessage(ps, pmes) dht.netChan.Outgoing <- mes } buf := new(bytes.Buffer) di := dht.getDiagInfo() buf.Write(di.Marshal()) // NOTE: this shouldnt be a hardcoded value after := time.After(time.Second * 20) count := len(seq) for count > 0 { select { case <-after: //Timeout, return what we have goto out case reqResp := <-listenChan: pmesOut := new(PBDHTMessage) err := proto.Unmarshal(reqResp.Data, pmesOut) if err != nil { // It broke? eh, whatever, keep going continue } buf.Write(reqResp.Data) count-- } } out: resp := Message{ Type: PBDHTMessage_DIAGNOSTIC, ID: pmes.GetId(), Value: buf.Bytes(), Response: true, } mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.netChan.Outgoing <- mes }
func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { pb := new(PBData) err := proto.Unmarshal(n.Data, pb) if err != nil { return nil, err } switch pb.GetType() { case PBData_Directory: return nil, ErrIsDir case PBData_File: return &DagReader{ node: n, serv: serv, buf: bytes.NewBuffer(pb.GetData()), }, nil case PBData_Raw: return bytes.NewBuffer(pb.GetData()), nil default: panic("Unrecognized node type!") } }
// Performs initial communication with this peer to share node ID's and // initiate communication. (secureIn, secureOut, error) func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { // Generate and send Hello packet. // Hello = (rand, PublicKey, Supported) nonce := make([]byte, 16) _, err := rand.Read(nonce) if err != nil { return nil, nil, err } hello := new(Hello) myPubKey, err := self.PubKey.Bytes() if err != nil { return nil, nil, err } hello.Rand = nonce hello.Pubkey = myPubKey hello.Exchanges = &SupportedExchanges hello.Ciphers = &SupportedCiphers hello.Hashes = &SupportedHashes encoded, err := proto.Marshal(hello) if err != nil { return nil, nil, err } out <- encoded // Parse their Hello packet and generate an Exchange packet. // Exchange = (EphemeralPubKey, Signature) resp := <-in helloResp := new(Hello) err = proto.Unmarshal(resp, helloResp) if err != nil { return nil, nil, err } remote.PubKey, err = ci.UnmarshalPublicKey(helloResp.GetPubkey()) if err != nil { return nil, nil, err } remote.ID, err = IDFromPubKey(remote.PubKey) if err != nil { return nil, nil, err } exchange, err := selectBest(SupportedExchanges, helloResp.GetExchanges()) if err != nil { return nil, nil, err } cipherType, err := selectBest(SupportedCiphers, helloResp.GetCiphers()) if err != nil { return nil, nil, err } hashType, err := selectBest(SupportedHashes, helloResp.GetHashes()) if err != nil { return nil, nil, err } epubkey, done, err := ci.GenerateEKeyPair(exchange) // Generate EphemeralPubKey if err != nil { return nil, nil, err } var handshake bytes.Buffer // Gather corpus to sign. handshake.Write(encoded) handshake.Write(resp) handshake.Write(epubkey) exPacket := new(Exchange) exPacket.Epubkey = epubkey exPacket.Signature, err = self.PrivKey.Sign(handshake.Bytes()) if err != nil { return nil, nil, err } exEncoded, err := proto.Marshal(exPacket) if err != nil { return nil, nil, err } out <- exEncoded // Parse their Exchange packet and generate a Finish packet. // Finish = E('Finish') resp1 := <-in exchangeResp := new(Exchange) err = proto.Unmarshal(resp1, exchangeResp) if err != nil { return nil, nil, err } var theirHandshake bytes.Buffer _, err = theirHandshake.Write(resp) if err != nil { return nil, nil, err } _, err = theirHandshake.Write(encoded) if err != nil { return nil, nil, err } _, err = theirHandshake.Write(exchangeResp.GetEpubkey()) if err != nil { return nil, nil, err } ok, err := remote.PubKey.Verify(theirHandshake.Bytes(), exchangeResp.GetSignature()) if err != nil { return nil, nil, err } if !ok { return nil, nil, errors.New("Bad signature!") } secret, err := done(exchangeResp.GetEpubkey()) if err != nil { return nil, nil, err } cmp := bytes.Compare(myPubKey, helloResp.GetPubkey()) mIV, tIV, mCKey, tCKey, mMKey, tMKey := ci.KeyStretcher(cmp, cipherType, hashType, secret) secureIn := make(chan []byte) secureOut := make(chan []byte) go secureInProxy(in, secureIn, hashType, tIV, tCKey, tMKey) go secureOutProxy(out, secureOut, hashType, mIV, mCKey, mMKey) finished := []byte("Finished") secureOut <- finished resp2 := <-secureIn if bytes.Compare(resp2, finished) != 0 { return nil, nil, errors.New("Negotiation failed.") } u.DOut("[%s] identify: Got node id: %s\n", self.ID.Pretty(), remote.ID.Pretty()) return secureIn, secureOut, nil }
func TestGetFailures(t *testing.T) { fn := newFauxNet() fn.Listen() local := new(peer.Peer) local.ID = peer.ID("test_peer") d := NewDHT(local, fn, ds.NewMapDatastore()) other := &peer.Peer{ID: peer.ID("other_peer")} d.Start() d.Update(other) // This one should time out _, err := d.GetValue(u.Key("test"), time.Millisecond*10) if err != nil { if err != u.ErrTimeout { t.Fatal("Got different error than we expected.") } } else { t.Fatal("Did not get expected error!") } // Reply with failures to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { t.Fatal(err) } resp := Message{ Type: pmes.GetType(), ID: pmes.GetId(), Response: true, Success: false, } return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) }) // This one should fail with NotFound _, err = d.GetValue(u.Key("test"), time.Millisecond*1000) if err != nil { if err != u.ErrNotFound { t.Fatalf("Expected ErrNotFound, got: %s", err) } } else { t.Fatal("expected error, got none.") } success := make(chan struct{}) fn.handlers = nil fn.AddHandler(func(mes *swarm.Message) *swarm.Message { resp := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, resp) if err != nil { t.Fatal(err) } if resp.GetSuccess() { t.Fatal("Get returned success when it shouldnt have.") } success <- struct{}{} return nil }) // Now we test this DHT's handleGetValue failure req := Message{ Type: PBDHTMessage_GET_VALUE, Key: "hello", ID: swarm.GenerateMessageID(), Value: []byte{0}, } fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf()) <-success }