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...)) }
// Requests the next piece of metadata we don't yet have from this peer. // Must only be called if we know the peer supports it. // Does nothing if we already have all of the pieces they claim. func (p *swarmPeer) requestNextMetadataPiece() { if !p.infoComplete { i := len(p.infoPieces) requestBody, err := bencoding.Encode(bencoding.Dict{ "msg_type": bencoding.Int(0), // request piece "piece": bencoding.Int(i), }) if err != nil { logger.Error("unable to encode extension metadata request: %v", err) return } logger.Notice("requesting piece %v of metadata!", i) p.writeMessage(msgExtended, append([]byte{p.extensions.bep9MetadataExchange.id}, requestBody...)) } }
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 (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 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 }