// Scrape reports scrape for one or more files, using HTTP format func (h HTTPTracker) Scrape(files []data.FileRecord) []byte { // Response struct scrape := scrapeResponse{ Files: make(map[string]scrapeFile), } // WaitGroup to wait for all scrape file entries to be generated var wg sync.WaitGroup wg.Add(len(files)) // Mutex for safe locking on map writes var mutex sync.RWMutex // Iterate all files in parallel for _, f := range files { go func(f data.FileRecord, scrape *scrapeResponse, mutex *sync.RWMutex, wg *sync.WaitGroup) { // Generate scrapeFile struct fileInfo := scrapeFile{} var err error // Seeders count fileInfo.Complete, err = f.Seeders() if err != nil { log.Println(err.Error()) } // Completion count fileInfo.Downloaded, err = f.Completed() if err != nil { log.Println(err.Error()) } // Leechers count fileInfo.Incomplete, err = f.Leechers() if err != nil { log.Println(err.Error()) } // Add hash and file info to map mutex.Lock() scrape.Files[f.InfoHash] = fileInfo mutex.Unlock() // Inform waitgroup that this file is ready wg.Done() }(f, &scrape, &mutex, &wg) } // Wait for all information to be generated wg.Wait() // Marshal struct into bencode buf := bytes.NewBuffer(make([]byte, 0)) if err := bencode.Marshal(buf, scrape); err != nil { log.Println(err.Error()) return h.Error(ErrScrapeFailure.Error()) } return buf.Bytes() }
func (s *share) processRequest(msg []byte) ([]byte, error) { fmt.Println("process request") var r Request err := bencode.Unmarshal(bytes.NewBuffer(msg), &r) if err != nil { return []byte{}, err } mdata, err := s.getFileMeta(r.File) check(err) if r.Index == -1 && r.Begin == -1 && r.Length == -1 { fmt.Println("want some meta") var data bytes.Buffer err = bencode.Marshal(&data, *mdata) fmt.Println(mdata) check(err) return s.createPiece(r.File, -1, -1, data.Bytes()), nil } buf := make([]byte, r.Length) f, err := os.Open(s.Path + "/" + r.File) check(err) _, err = f.ReadAt(buf, int64(r.Index*mdata.Piece_length+r.Begin)) f.Close() check(err) fmt.Println("sending piece for", r.File, r.Index, r.Begin) return s.createPiece(r.File, r.Index, r.Begin, buf), nil }
// Scrape reports scrape for one or more files, using HTTP format func (h HTTPTracker) Scrape(files []data.FileRecord) []byte { // Response struct scrape := scrapeResponse{ Files: make(map[string]scrapeFile), } // Iterate all files for _, file := range files { // Generate scrapeFile struct fileInfo := scrapeFile{ Complete: file.Seeders(), Downloaded: file.Completed(), Incomplete: file.Leechers(), } // Add hash and file info to map scrape.Files[file.InfoHash] = fileInfo } // Marshal struct into bencode buf := bytes.NewBuffer(make([]byte, 0)) if err := bencode.Marshal(buf, scrape); err != nil { log.Println(err.Error()) return h.Error("Tracker error: failed to create scrape response") } return buf.Bytes() }
// Encode to Bencode, but only encode non-default values. func (m *MetaInfo) Bencode(w io.Writer) (err error) { var mi map[string]interface{} = map[string]interface{}{} id := m.Info.toMap() if len(id) > 0 { mi["info"] = id } // Do not encode InfoHash. Clients are supposed to calculate it themselves. if m.Announce != "" { mi["announce"] = m.Announce } if len(m.AnnounceList) > 0 { mi["announce-list"] = m.AnnounceList } if m.CreationDate != "" { mi["creation date"] = m.CreationDate } if m.Comment != "" { mi["comment"] = m.Comment } if m.CreatedBy != "" { mi["created by"] = m.CreatedBy } if m.Encoding != "" { mi["encoding"] = m.Encoding } bencode.Marshal(w, mi) return }
// Announce announces using HTTP format func (h HTTPTracker) Announce(query url.Values, file data.FileRecord) []byte { // Generate response struct announce := AnnounceResponse{ Interval: common.Static.Config.Interval, MinInterval: common.Static.Config.Interval / 2, } // Get seeders count on file var err error announce.Complete, err = file.Seeders() if err != nil { log.Println(err.Error()) } // Get leechers count on file announce.Incomplete, err = file.Leechers() if err != nil { log.Println(err.Error()) } // Check for numwant parameter, return up to that number of peers // Default is 50 per protocol numwant := 50 if query.Get("numwant") != "" { // Verify numwant is an integer num, err := strconv.Atoi(query.Get("numwant")) if err == nil { numwant = num } } // Marshal struct into bencode buf := bytes.NewBuffer(make([]byte, 0)) if err := bencode.Marshal(buf, announce); err != nil { log.Println(err.Error()) return h.Error(ErrAnnounceFailure.Error()) } // Generate compact peer list of length numwant // Note: because we are HTTP, we can mark second parameter as 'true' to get a // more accurate peer list compactPeers, err := file.CompactPeerList(numwant, true) if err != nil { log.Println(err.Error()) return h.Error(ErrPeerListFailure.Error()) } // Because the bencode marshaler does not handle compact, binary peer list conversion, // we handle it manually here. // Get initial buffer, chop off 3 bytes: "0:e", append the actual list length with new colon out := buf.Bytes() out = append(out[0:len(out)-3], []byte(strconv.Itoa(len(compactPeers))+":")...) // Append peers list, terminate with an "e" return append(append(out, compactPeers...), byte('e')) }
// sendMsg bencodes the data in 'query' and sends it to the remote node. func sendMsg(conn *net.UDPConn, raddr *net.UDPAddr, query interface{}) { totalSent.Add(1) var b bytes.Buffer if err := bencode.Marshal(&b, query); err != nil { return } if _, err := conn.WriteToUDP(b.Bytes(), raddr); err != nil { // debug.Println("DHT: node write failed:", err) } return }
func (t *Tracker) handleScrape(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") infoHashes := r.URL.Query()["info_hash"] response := make(bmap) response["files"] = t.t.scrape(infoHashes) var b bytes.Buffer err := bencode.Marshal(&b, response) if err == nil { w.Write(b.Bytes()) } }
func (s *share) createPing(secret string) []byte { buf := bytes.NewBuffer([]byte("DBIT")) err := bencode.Marshal(buf, Header{ "ping", *port, fmt.Sprintf("%s", sha1.Sum([]byte(secret))), fmt.Sprintf("%s", sha1.Sum([]byte("192.168.1.64:6667"))), }) check(err) return buf.Bytes() }
// httpTrackerAnnounce announces using HTTP format func httpTrackerAnnounce(query url.Values, file fileRecord, fileUser fileUserRecord) []byte { // Generate response struct announce := announceResponse{ Complete: file.Seeders(), Incomplete: file.Leechers(), } // If client has not yet completed torrent, ask them to announce more frequently, so they can gather // more peers and quickly report their statistics if fileUser.Completed == false { announce.Interval = 600 announce.MinInterval = 300 } else { // Once a torrent has been completed, report statistics less frequently announce.Interval = randRange(static.Config.Interval-600, static.Config.Interval) announce.MinInterval = static.Config.Interval / 2 } // Check for numwant parameter, return up to that number of peers // Default is 50 per protocol numwant := 50 if query.Get("numwant") != "" { // Verify numwant is an integer num, err := strconv.Atoi(query.Get("numwant")) if err == nil { numwant = num } } // Marshal struct into bencode buf := bytes.NewBuffer(make([]byte, 0)) if err := bencode.Marshal(buf, announce); err != nil { log.Println(err.Error()) return httpTrackerError("Tracker error: failed to create announce response") } // Generate compact peer list of length numwant, exclude this user peers := file.PeerList(query.Get("ip"), numwant) // Because the bencode marshaler does not handle compact, binary peer list conversion, // we handle it manually here. // Get initial buffer, chop off 3 bytes: "0:e", append the actual list length with new colon out := buf.Bytes() out = append(out[0:len(out)-3], []byte(strconv.Itoa(len(peers))+":")...) // Append peers list, terminate with an "e" out = append(append(out, peers...), byte('e')) // Return final announce message return out }
func (t *Tracker) handleAnnounce(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") response := make(bmap) var params announceParams var peerListenAddress *net.TCPAddr err := params.parse(r.URL) if err == nil { if params.trackerID != "" && params.trackerID != t.ID { err = fmt.Errorf("Incorrect tracker ID: %#v", params.trackerID) } } if err == nil { peerListenAddress, err = newTrackerPeerListenAddress(r.RemoteAddr, ¶ms) } if err == nil { now := time.Now() t.m.Lock() err = t.t.handleAnnounce(now, peerListenAddress, ¶ms, response) t.m.Unlock() if err == nil { response["interval"] = int64(30 * 60) response["tracker id"] = t.ID } } var b bytes.Buffer if err != nil { log.Printf("announce from %v failed: %#v", r.RemoteAddr, err.Error()) errorResponse := make(bmap) errorResponse["failure reason"] = err.Error() err = bencode.Marshal(&b, errorResponse) } else { err = bencode.Marshal(&b, response) } if err == nil { w.Write(b.Bytes()) } }
func (s *share) createPiece(path string, index, begin int, piece []byte) []byte { b := bytes.NewBuffer([]byte("DBIT")) err := bencode.Marshal(b, Piece{ "piece", *port, fmt.Sprintf("%s", sha1.Sum([]byte(s.Secret))), fmt.Sprintf("%s", sha1.Sum([]byte("192.168.1.64:6667"))), path, index, begin, fmt.Sprintf("%s", piece), }) check(err) return b.Bytes() }
func (s *share) createRequest(path string, index, begin, length int) []byte { b := bytes.NewBuffer([]byte("DBIT")) err := bencode.Marshal(b, Request{ "req", *port, fmt.Sprintf("%s", sha1.Sum([]byte(s.Secret))), fmt.Sprintf("%s", sha1.Sum([]byte("192.168.1.64:6667"))), path, index, begin, length, }) check(err) return b.Bytes() }
// Updates the InfoHash field. Call this after manually changing the Info data. func (m *MetaInfo) UpdateInfoHash(metaInfo *MetaInfo) (err error) { var b bytes.Buffer infoMap := m.Info.toMap() if len(infoMap) > 0 { err = bencode.Marshal(&b, infoMap) if err != nil { return } } hash := sha1.New() hash.Write(b.Bytes()) m.InfoHash = string(hash.Sum(nil)) return }
// Error reports a bencoded []byte response as specified by input string func (h HTTPTracker) Error(err string) []byte { res := errorResponse{ FailureReason: err, Interval: common.Static.Config.Interval, MinInterval: common.Static.Config.Interval / 2, } // Marshal struct into bencode buf := bytes.NewBuffer(make([]byte, 0)) if err := bencode.Marshal(buf, res); err != nil { log.Println(err.Error()) return nil } return buf.Bytes() }
// Announce announces using HTTP format func (h HTTPTracker) Announce(query url.Values, file data.FileRecord) []byte { // Generate response struct announce := AnnounceResponse{ Complete: file.Seeders(), Incomplete: file.Leechers(), Interval: common.Static.Config.Interval, MinInterval: common.Static.Config.Interval / 2, } // Check for numwant parameter, return up to that number of peers // Default is 50 per protocol numwant := 50 if query.Get("numwant") != "" { // Verify numwant is an integer num, err := strconv.Atoi(query.Get("numwant")) if err == nil { numwant = num } } // Marshal struct into bencode buf := bytes.NewBuffer(make([]byte, 0)) if err := bencode.Marshal(buf, announce); err != nil { log.Println(err.Error()) return h.Error("Tracker error: failed to create announce response") } // Generate compact peer list of length numwant, exclude this user peers := file.PeerList(query.Get("ip"), numwant) // Because the bencode marshaler does not handle compact, binary peer list conversion, // we handle it manually here. // Get initial buffer, chop off 3 bytes: "0:e", append the actual list length with new colon out := buf.Bytes() out = append(out[0:len(out)-3], []byte(strconv.Itoa(len(peers))+":")...) // Append peers list, terminate with an "e" out = append(append(out, peers...), byte('e')) // Return final announce message return out }
//analagous to getTracker //TODO do this recursively //gotta find peers first func (s *share) createMetaShake() []byte { files := s.getFileHashes() peers := make([]string, 0) for p, _ := range s.peers { peers = append(peers, p) } b := bytes.NewBuffer([]byte("DBIT")) err := bencode.Marshal(b, Shake{ "meta", *port, fmt.Sprintf("%s", sha1.Sum([]byte(s.Secret))), fmt.Sprintf("%s", sha1.Sum([]byte("192.168.1.64:6667"))), //FIXME config? files, peers, }) check(err) return b.Bytes() }
func (p *peerState) sendMetadataRequest(piece int) { log.Printf("Sending metadata request for piece %d to %s\n", piece, p.address) m := map[string]int{ "msg_type": METADATA_REQUEST, "piece": piece, } var raw bytes.Buffer err := bencode.Marshal(&raw, m) if err != nil { return } msg := make([]byte, raw.Len()+2) msg[0] = EXTENSION msg[1] = byte(p.theirExtensions["ut_metadata"]) copy(msg[2:], raw.Bytes()) p.sendMessage(msg) }
func (p *peerState) SendExtensions(port int) { handshake := map[string]interface{}{ "m": map[string]int{ "ut_metadata": 1, }, "v": "Taipei-Torrent dev", } var buf bytes.Buffer err := bencode.Marshal(&buf, handshake) if err != nil { //log.Println("Error when marshalling extension message") return } msg := make([]byte, 2+buf.Len()) msg[0] = EXTENSION msg[1] = EXTENSION_HANDSHAKE copy(msg[2:], buf.Bytes()) p.sendMessage(msg) }
func getMetaInfo(torrent string) (metaInfo *MetaInfo, err error) { var input io.ReadCloser if strings.HasPrefix(torrent, "http:") { r, err := proxyHttpGet(torrent) if err != nil { return nil, err } input = r.Body } else if strings.HasPrefix(torrent, "magnet:") { magnet, err := parseMagnet(torrent) if err != nil { log.Println("Couldn't parse magnet: ", err) return nil, err } ih, err := dht.DecodeInfoHash(magnet.InfoHashes[0]) if err != nil { return nil, err } metaInfo = &MetaInfo{InfoHash: string(ih)} return metaInfo, err } else { if input, err = os.Open(torrent); err != nil { return } } // We need to calcuate the sha1 of the Info map, including every value in the // map. The easiest way to do this is to read the data using the Decode // API, and then pick through it manually. var m interface{} m, err = bencode.Decode(input) input.Close() if err != nil { err = errors.New("Couldn't parse torrent file phase 1: " + err.Error()) return } topMap, ok := m.(map[string]interface{}) if !ok { err = errors.New("Couldn't parse torrent file phase 2.") return } infoMap, ok := topMap["info"] if !ok { err = errors.New("Couldn't parse torrent file. info") return } var b bytes.Buffer if err = bencode.Marshal(&b, infoMap); err != nil { return } hash := sha1.New() hash.Write(b.Bytes()) var m2 MetaInfo err = bencode.Unmarshal(&b, &m2.Info) if err != nil { return } m2.InfoHash = string(hash.Sum(nil)) m2.Announce = getString(topMap, "announce") m2.AnnounceList = getSliceSliceString(topMap, "announce-list") m2.CreationDate = getString(topMap, "creation date") m2.Comment = getString(topMap, "comment") m2.CreatedBy = getString(topMap, "created by") m2.Encoding = getString(topMap, "encoding") metaInfo = &m2 return }
func (sections *SectionList) Write(target io.Writer) (err error) { return bencode.Marshal(target, *sections) }
func loadShare(secret string, s *share) { newShare := false //if file doesn't exist, need to drop some tables //has to be done here because sql.Open will make a file if _, err := os.Stat(secret + ".db"); os.IsNotExist(err) { fmt.Println("new share") newShare = true } //hopefully reload config will not reload the database if shares[secret].Db == nil { fmt.Println("no db") db, err := sql.Open("sqlite3", secret+".db") check(err) s.Db = db shares[secret] = *s //shares[secret] = share{Secret: secret, Path: s.Path, Db: db, peers: s.peers} //s = shares[secret] } //drop some tables if newShare { //TODO potential bottleneck here, walk is slow -- but must be done somehow // it appears there are about 20 ways to do io in stdlib //TODO time not in sync db, they parse bencoding -- maybe a good idea? // allows: select * from files where time > x; x = most recent, gets all new _, err := s.Db.Exec( `CREATE TABLE files ( path TEXT NOT NULL PRIMARY KEY, data BLOB NOT NULL);`) check(err) } insert, err := s.Db.Prepare("INSERT INTO files(path, data) values(?, ?)") check(err) update, err := s.Db.Prepare("UPDATE files SET data = ? WHERE path = ?") check(err) filepath.Walk(s.Path, func(path string, f os.FileInfo, err error) error { if f.IsDir() { return nil } relPath := path[len(s.Path)+1:] //slice abs + / off fmt.Println(relPath) updated := false if !newShare { btf, err := s.getFileMeta(relPath) check(err) if btf != nil && f.ModTime().Unix() > btf.Time { updated = true } else if btf != nil { return nil //if not update & we have something, we can leave } //if nil, new file.. so treat same } btf, err := createFileMeta(path, f) check(err) fmt.Println(path) var b bytes.Buffer err = bencode.Marshal(&b, btf) check(err) if updated { _, err = update.Exec(relPath, b.Bytes()) check(err) fmt.Println("update") } else { _, err = insert.Exec(relPath, b.Bytes()) fmt.Println("not update") check(err) } return nil }) }