예제 #1
0
파일: rpc.go 프로젝트: jbitor/bittorrent
func (local *localNode) sendQuery(remote *RemoteNode, queryType string, arguments bencoding.Dict) (query *RpcQuery) {
	// XXX(JB): These should probably have a distinct warning logger.
	if remote.Flooded() {
		logger.Info("WARNING: flooding node %v.", remote)
	}

	if remote.Status() == STATUS_BAD {
		logger.Info("WARNING: querying bad node %v.", remote)
	}

	query = new(RpcQuery)
	query.Result = make(chan *bencoding.Dict)
	query.Err = make(chan error)
	query.Remote = remote

	if arguments == nil {
		arguments = bencoding.Dict{}
	}

	arguments["id"] = bencoding.String(local.Id)

	// XXX: assert that these keys are not already present?
	message := bencoding.Dict{
		"y": bencoding.String("q"),
		"q": bencoding.String(queryType),
		"a": arguments,
	}

	transactionId := new([4]byte)
	if _, err := rand.Read(transactionId[:]); err != nil {
		query.Err <- err
		close(query.Result)
		close(query.Err)
		return
	}

	query.TransactionId = string(transactionId[:])

	local.OutstandingQueries[query.TransactionId] = query

	message["t"] = bencoding.String(query.TransactionId)

	encodedMessage, err := bencoding.Encode(message)

	if err != nil {
		query.Err <- err
		close(query.Result)
		close(query.Err)
		return
	}

	remote.LastRequestTo = time.Now()

	go func() {
		// XXX: Does this wait longer than necessary to send the packet?
		local.Connection.WriteTo(encodedMessage, &remote.Address)
	}()

	return query
}
예제 #2
0
파일: peer.go 프로젝트: jbitor/bittorrent
func (p *swarmPeer) writeHandshake(peerId BTID, infohash BTID) {
	// BitTorrent Handshake
	header := []byte(peerProtocolHeader)
	extensionFlags := make([]byte, 8)
	extensionFlags[5] |= 0x10 // indicate extension protocol support
	p.conn.Write(header)
	p.conn.Write(extensionFlags)
	p.conn.Write([]byte(infohash))
	p.conn.Write([]byte(peerId))

	// If we had any pieces, we would need to indicate so here, but we don't.
	// writeMessage(w, msgBitfield, piecesBitfield)

	// TODO: move this somewhere else and only fire it after we check their extension flags
	// Write Extension Protcol Handshake

	handshakeBody, err := bencoding.Encode(bencoding.Dict{
		"v": bencoding.String("jbitor 0.0.0"),
		"m": bencoding.Dict{
			"ut_metadata": bencoding.Int(ourUtMetadataId),
			"ut_pex":      bencoding.Int(ourUtPexId),
		},
		"p": bencoding.Int(PORT),
	})

	if err != nil {
		logger.Info("unable to encode extension handshake: %v", err)
		return
	}

	logger.Info("sent extension handshake")
	p.writeMessage(msgExtended, append([]byte{extensionHandshakeId}, handshakeBody...))
}
예제 #3
0
파일: main.go 프로젝트: jbitor/cli
func main() {
	loggerconfig.Use()

	if len(os.Args) != 2 {
		logger.Fatalf("Usage: %v PATH", os.Args[0])
		return
	}

	weakrand.Seed(time.Now().UTC().UnixNano())

	path := os.Args[1]

	infoDict, err := bittorrent.GenerateTorrentMetaInfo(bittorrent.CreationOptions{
		Path:           path,
		PieceLength:    PieceLength,
		ForceMultiFile: false,
	})
	if err != nil {
		logger.Fatalf("Error generating torrent: %v", err)
		return
	}

	infoData, err := bencoding.Encode(infoDict)
	if err != nil {
		logger.Fatalf("Error encoding torrent infodict (for hashing): %v", err)
		return
	}

	torrentDict := bencoding.Dict{
		"info": infoDict,
		"nodes": bencoding.List{
			bencoding.List{
				bencoding.String("127.0.0.1"),
				bencoding.Int(6881),
			},
		},
	}

	torrentData, err := bencoding.Encode(torrentDict)

	if err != nil {
		logger.Fatalf("Error encoding torrent data: %v", err)
		return
	}

	hasher := sha1.New()
	hasher.Write(infoData)
	hash := hasher.Sum(nil)
	infoHash := bittorrent.BTID(hash)

	logger.Info("Generated torrent btih=%v.", infoHash)

	os.Stdout.Write(torrentData)
	os.Stdout.Sync()
}
예제 #4
0
파일: rpc.go 프로젝트: jbitor/bittorrent
func encodeNodeAddress(addr net.UDPAddr) (encoded bencoding.String) {
	if addr.Port >= (1<<32) || addr.Port < 0 {
		panic("Port out of bounds?")
	}

	ip4 := addr.IP.To4()

	return bencoding.String([]byte{
		ip4[0],
		ip4[1],
		ip4[2],
		ip4[3],
		byte((addr.Port >> 8) & 0xFF),
		byte(addr.Port & 0xFF),
	})
}
예제 #5
0
func (local *localNode) MarshalBencodingDict() (dict bencoding.Dict) {
	dict = bencoding.Dict{}

	if local.Id != "" {
		dict["Id"] = bencoding.String(local.Id)
	}

	dict["Port"] = bencoding.Int(local.Port)

	nodes := make(bencoding.List, len(local.Nodes))

	i := 0
	for _, node := range local.Nodes {
		nodes[i] = node.MarshalBencodingDict()
		i++
	}

	dict["Nodes"] = nodes

	return dict
}
예제 #6
0
파일: rpc.go 프로젝트: jbitor/bittorrent
func (local *localNode) FindNode(remote *RemoteNode, id bittorrent.BTID) (<-chan []*RemoteNode, <-chan error) {
	findResult := make(chan []*RemoteNode)
	findErr := make(chan error)

	query := local.sendQuery(remote, "find_node", bencoding.Dict{
		"target": bencoding.String(id),
	})

	go func() {
		defer close(findErr)
		defer close(findResult)

		select {
		case value := <-query.Result:
			nodesData, ok := (*value)["nodes"].(bencoding.String)
			if !ok {
				remote.ConsecutiveFailedQueries++
				findErr <- errors.New(".nodes string does not exist")
				return
			}

			result, err := local.decodeNodesString(nodesData, remote)
			if err != nil {
				findErr <- err
				return
			}

			remote.ConsecutiveFailedQueries = 0
			findResult <- result

		case err := <-query.Err:
			remote.ConsecutiveFailedQueries++
			findErr <- err
		}
	}()

	return findResult, findErr
}
예제 #7
0
파일: rpc.go 프로젝트: jbitor/bittorrent
func (local *localNode) GetPeers(remote *RemoteNode, infoHash bittorrent.BTID) (<-chan []net.TCPAddr, <-chan []*RemoteNode, <-chan error) {
	peersResult := make(chan []net.TCPAddr)
	nodesResult := make(chan []*RemoteNode)
	getPeersErr := make(chan error)

	query := local.sendQuery(remote, "get_peers", bencoding.Dict{
		"info_hash": bencoding.String(infoHash),
	})

	go func() {
		defer close(peersResult)
		defer close(nodesResult)
		defer close(getPeersErr)

		select {
		case value := <-query.Result:
			peerData, peersOk := (*value)["values"].(bencoding.List)
			nodesData, nodesOk := (*value)["nodes"].(bencoding.String)

			if peersOk {
				result := make([]net.TCPAddr, len(peerData))

				for i, data := range peerData {
					dataStr, ok := data.(bencoding.String)
					if !ok {
						getPeersErr <- errors.New(".values contained non-string")
						remote.ConsecutiveFailedQueries++
						return
					}

					addr, err := bittorrent.DecodePeerAddress(dataStr)
					if err != nil {
						remote.ConsecutiveFailedQueries++
						getPeersErr <- err
						return
					}

					result[i] = addr
				}

				remote.ConsecutiveFailedQueries = 0

				peersResult <- result
			} else if nodesOk {
				result, err := local.decodeNodesString(nodesData, remote)
				if err != nil {
					remote.ConsecutiveFailedQueries++
					getPeersErr <- err
					return
				}

				remote.ConsecutiveFailedQueries = 0
				nodesResult <- result
			} else {
				remote.ConsecutiveFailedQueries++
				getPeersErr <- errors.New(fmt.Sprintf("response did not include peer or node list - %v", *value))
			}

		case err := <-query.Err:
			remote.ConsecutiveFailedQueries++
			getPeersErr <- err
		}
	}()

	return peersResult, nodesResult, getPeersErr
}
예제 #8
0
func GenerateTorrentMetaInfo(options CreationOptions) (TorrentMeta, error) {
	fileInfo, err := os.Stat(options.Path)
	if err != nil {
		return nil, err
	}

	multiFile := fileInfo.IsDir() || options.ForceMultiFile

	pieces := make([]byte, 0)

	fileList := bencoding.List{}

	pieceHasher := newPieceHasher(options.PieceLength)

	if multiFile {
		err := filepath.Walk(options.Path, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if info.IsDir() {
				return nil
			}

			relPath, err := filepath.Rel(options.Path, path)
			if err != nil {
				return err
			}

			pathList := bencoding.List{}

			for _, component := range filepath.SplitList(relPath) {
				pathList = append(pathList, bencoding.String(component))
			}

			fileDict := TorrentFileMeta{
				"path":   pathList,
				"length": bencoding.Int(info.Size()),
			}

			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			pieceHasher.Write(data)

			fileList = append(fileList, fileDict)

			return nil
		})

		if err != nil {
			return nil, err
		}
	} else {
		if fileInfo.Size() > 0 {
			file, err := os.Open(options.Path)
			if err != nil {
				return nil, err
			}

			defer file.Close()
			io.Copy(pieceHasher.Writer(), file)
		}
	}

	pieces = pieceHasher.Pieces()

	infoDict := TorrentMeta{
		"name":         bencoding.String(fileInfo.Name()),
		"piece length": bencoding.Int(options.PieceLength),
		"pieces":       bencoding.String(pieces),
	}

	if multiFile {
		infoDict["files"] = fileList
	} else {
		infoDict["length"] = bencoding.Int(fileInfo.Size())
	}

	return infoDict, nil
}