Example #1
0
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
}
Example #2
0
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)
	}
}