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 }
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...)) }
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() }
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), }) }
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 }
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 }
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 }
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 }