Пример #1
0
// 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()
}
Пример #2
0
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
}
Пример #3
0
// 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()
}
Пример #4
0
// 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
}
Пример #5
0
// 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'))
}
Пример #6
0
// 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
}
Пример #7
0
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())
	}
}
Пример #8
0
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()
}
Пример #9
0
// 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
}
Пример #10
0
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, &params)
	}
	if err == nil {
		now := time.Now()
		t.m.Lock()
		err = t.t.handleAnnounce(now, peerListenAddress, &params, 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())
	}
}
Пример #11
0
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()
}
Пример #12
0
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()
}
Пример #13
0
// 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
}
Пример #14
0
// 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()
}
Пример #15
0
// 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
}
Пример #16
0
//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()
}
Пример #17
0
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)
}
Пример #18
0
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)
}
Пример #19
0
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
}
Пример #20
0
func (sections *SectionList) Write(target io.Writer) (err error) {

	return bencode.Marshal(target, *sections)
}
Пример #21
0
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
	})
}