func (t *TorrentSession) generalMessage(message []byte, p *peerState) (err error) { messageId := message[0] switch messageId { case CHOKE: // log.Println("choke", p.address) if len(message) != 1 { return errors.New("Unexpected length") } err = t.doChoke(p) case UNCHOKE: // log.Println("unchoke", p.address) if len(message) != 1 { return errors.New("Unexpected length") } p.peer_choking = false for i := 0; i < MAX_OUR_REQUESTS; i++ { err = t.RequestBlock(p) if err != nil { return } } case INTERESTED: // log.Println("interested", p) if len(message) != 1 { return errors.New("Unexpected length") } p.peer_interested = true // TODO: Consider better unchoking policy (this is needed for // clients like Transmission who don't send a BITFIELD so we have to // unchoke them at this moment) p.SetChoke(false) case NOT_INTERESTED: // log.Println("not interested", p) if len(message) != 1 { return errors.New("Unexpected length") } p.peer_interested = false case HAVE: if len(message) != 5 { return errors.New("Unexpected length") } piece := binary.BigEndian.Uint32(message[1:]) if p.have.IsWithinLimits(int(piece)) { log.Printf("[TORRENT] Set have at %d for %s\n", piece, p.address) p.have.Set(int(piece)) if !p.am_interested && !t.pieceSet.IsSet(int(piece)) { p.SetInterested(true) log.Printf("[TORRENT] %s has %d, asking for it", p.address, piece) // TODO DRY up, this is a copy paste from RequestBlock pieceLength := int(t.m.Info.PieceLength) if int(piece) == t.totalPieces-1 { pieceLength = t.lastPieceLength } pieceCount := (pieceLength + STANDARD_BLOCK_LENGTH - 1) / STANDARD_BLOCK_LENGTH t.activePieces[int(piece)] = &ActivePiece{make([]int, pieceCount), pieceLength} t.RequestBlock2(p, int(piece), false) } } else { return errors.New("have index is out of range.") } case BITFIELD: // log.Println("bitfield", p.address) if !p.can_receive_bitfield { return errors.New("Late bitfield operation") } p.SetChoke(false) // TODO: better choke policy p.have = bitset.NewFromBytes(t.totalPieces, message[1:]) if p.have == nil { return errors.New("Invalid bitfield data.") } t.checkInteresting(p) p.can_receive_bitfield = false if p.peer_choking == false { for i := 0; i < MAX_OUR_REQUESTS; i++ { err = t.RequestBlock(p) if err != nil { return } } } case REQUEST: if len(message) != 13 { return errors.New("Unexpected message length") } index := binary.BigEndian.Uint32(message[1:5]) begin := binary.BigEndian.Uint32(message[5:9]) length := binary.BigEndian.Uint32(message[9:13]) if !p.have.IsWithinLimits(int(index)) { return errors.New("piece out of range.") } if !t.pieceSet.IsSet(int(index)) { return errors.New("we don't have that piece.") } if int64(begin) >= t.m.Info.PieceLength { return errors.New("begin out of range.") } if int64(begin)+int64(length) > t.m.Info.PieceLength { return errors.New("begin + length out of range.") } // TODO: Asynchronous // p.AddRequest(index, begin, length) return t.sendRequest(p, index, begin, length) case PIECE: // piece if len(message) < 9 { return errors.New("unexpected message length") } index := binary.BigEndian.Uint32(message[1:5]) begin := binary.BigEndian.Uint32(message[5:9]) length := len(message) - 9 if !p.have.IsWithinLimits(int(index)) { return errors.New("piece out of range.") } if t.pieceSet.IsSet(int(index)) { // We already have that piece, keep going break } if int64(begin) >= t.m.Info.PieceLength { return errors.New("begin out of range.") } if int64(begin)+int64(length) > t.m.Info.PieceLength { return errors.New("begin + length out of range.") } if length > 128*1024 { return errors.New("Block length too large.") } globalOffset := int64(index)*t.m.Info.PieceLength + int64(begin) _, err = t.fileStore.WriteAt(message[9:], globalOffset) if err != nil { return err } t.RecordBlock(p, index, begin, uint32(length)) err = t.RequestBlock(p) case CANCEL: // log.Println("cancel") if len(message) != 13 { return errors.New("Unexpected message length") } index := binary.BigEndian.Uint32(message[1:5]) begin := binary.BigEndian.Uint32(message[5:9]) length := binary.BigEndian.Uint32(message[9:13]) if !p.have.IsWithinLimits(int(index)) { return errors.New("piece out of range.") } if !t.pieceSet.IsSet(int(index)) { return errors.New("we don't have that piece.") } if int64(begin) >= t.m.Info.PieceLength { return errors.New("begin out of range.") } if int64(begin)+int64(length) > t.m.Info.PieceLength { return errors.New("begin + length out of range.") } if length != STANDARD_BLOCK_LENGTH { return errors.New("Unexpected block length.") } p.CancelRequest(index, begin, length) case PORT: // TODO: Implement this message. // We see peers sending us 16K byte messages here, so // it seems that we don't understand what this is. if len(message) != 3 { return fmt.Errorf("Unexpected length for port message: %d", len(message)) } case EXTENSION: err := t.DoExtension(message[1:], p) if err != nil { log.Printf("Failed extensions for %s: %s\n", p.address, err) } default: return errors.New(fmt.Sprintf("Uknown message id: %d\n", messageId)) } return }
func (t *TorrentSession) DoMetadata(msg []byte, p *peerState) { var message MetadataMessage err := bencode.Unmarshal(bytes.NewReader(msg), &message) if err != nil { log.Println("Error when parsing metadata: ", err) return } mt := message.MsgType switch mt { case METADATA_REQUEST: if !t.si.HaveTorrent { break } rawInfo := t.m.RawInfo() from := message.Piece * METADATA_PIECE_SIZE // Piece asked must be between the first one and the last one. // Note that the last one will most of the time be smaller than // METADATA_PIECE_SIZE if from >= len(rawInfo) { log.Printf("%d is out of range. Not sending this\n", message.Piece) break } to := from + METADATA_PIECE_SIZE if to > len(rawInfo) { to = len(rawInfo) } if _, ok := p.theirExtensions["ut_metadata"]; !ok { log.Println("%s doesn't understand ut_metadata\n", p.address) break } respHeader := MetadataMessage{ MsgType: METADATA_DATA, Piece: message.Piece, TotalSize: len(rawInfo), } var resp bytes.Buffer resp.WriteByte(EXTENSION) resp.WriteByte(byte(p.theirExtensions["ut_metadata"])) err = bencode.Marshal(&resp, respHeader) if err != nil { log.Println("Couldn't header metadata response: ", err) break } resp.Write(rawInfo[from:to]) p.sendMessage(resp.Bytes()) case METADATA_DATA: if t.si.HaveTorrent { break } if message.TotalSize == 0 { log.Println("No metadata size, bailing out") return } if message.Piece >= len(t.si.ME.Pieces) { log.Printf("Rejecting invalid metadata piece %d, max is %d\n", message.Piece, len(t.si.ME.Pieces)-1) break } pieceSize := METADATA_PIECE_SIZE if message.Piece == len(t.si.ME.Pieces)-1 { pieceSize = message.TotalSize - (message.TotalSize/METADATA_PIECE_SIZE)*METADATA_PIECE_SIZE } t.si.ME.Pieces[message.Piece] = msg[len(msg)-pieceSize:] finished := true for idx, data := range t.si.ME.Pieces { if len(data) == 0 { p.sendMetadataRequest(idx) finished = false break } } if !finished { break } log.Println("Finished downloading metadata!") var full bytes.Buffer for _, piece := range t.si.ME.Pieces { full.Write(piece) } info := full.Bytes() // Verify sha sha := sha1.New() sha.Write(info) actual := string(sha.Sum(nil)) if actual != t.m.InfoHash { log.Println("Invalid metadata") log.Printf("Expected %x, got %x\n", t.m.InfoHash, actual) break } err = t.reload(info) if err != nil { return } if p.have == nil { if p.temporaryBitfield != nil { p.have = bitset.NewFromBytes(t.totalPieces, p.temporaryBitfield) p.temporaryBitfield = nil } else { p.have = bitset.New(t.totalPieces) } } if p.have == nil { log.Panic("Invalid bitfield data") } p.SendBitfield(t.pieceSet) case METADATA_REJECT: log.Printf("%d didn't want to send piece %d\n", p.address, message.Piece) default: log.Println("Didn't understand metadata extension type: ", mt) } }