initial clean up

This commit is contained in:
ap-pauloafonso 2021-01-19 00:53:30 -03:00
parent fe5c6bde7a
commit bcd8f91ebb
4 changed files with 159 additions and 161 deletions

View file

@ -1,17 +1,110 @@
package beencode package beencode
import ( import (
"crypto/sha1"
"net/url"
"strconv" "strconv"
) )
const ( const (
dictToken = byte('d') dictToken = byte('d')
numberToken = byte('i') numberToken = byte('i')
listToken = byte('l') listToken = byte('l')
endOfCollectionToken = byte('e') endOfCollectionToken = byte('e')
lengthValueStringSeparator = byte(':') lengthValueStringSeparatorToken = byte(':')
torrentInfoKey = "info"
torrentNameKey = "name"
torrentPieceLengthKey = "piece length"
torrentLengthKey = "length"
torrentFilesKey = "files"
mainAnnounceKey = "announce"
announceListKey = "announce-list"
torrentDictOffsetsKey = "byte_offsets"
) )
// TorrentInfo contains all relevant information extracted from a beencode file
type TorrentInfo struct {
Name string
PieceSize int
TotalSize int
TrackerInfo *TrackerInfo
InfoHashURLEncoded string
}
//TrackerInfo contains http urls from the tracker
type TrackerInfo struct {
Main string
Urls []string
}
type torrentDict struct {
resultMap map[string]interface{}
}
//TorrentDictParse decodes the beencoded bytes and builds the torrentInfo file
func TorrentDictParse(dat []byte) (*TorrentInfo, error) {
dict, _ := mapParse(0, &dat)
torrentMap := torrentDict{resultMap: dict}
return &TorrentInfo{
Name: torrentMap.resultMap[torrentInfoKey].(map[string]interface{})[torrentNameKey].(string),
PieceSize: torrentMap.resultMap[torrentInfoKey].(map[string]interface{})[torrentPieceLengthKey].(int),
TotalSize: torrentMap.extractTotalSize(),
TrackerInfo: torrentMap.extractTrackerInfo(),
InfoHashURLEncoded: torrentMap.extractInfoHashURLEncoded(dat),
}, nil
}
func (T *torrentDict) extractInfoHashURLEncoded(rawData []byte) string {
byteOffsets := T.resultMap[torrentInfoKey].(map[string]interface{})[torrentDictOffsetsKey].([]int)
h := sha1.New()
h.Write([]byte(rawData[byteOffsets[0]:byteOffsets[1]]))
ret := h.Sum(nil)
return url.QueryEscape(string(ret))
}
func (T *torrentDict) extractTotalSize() int {
if value, ok := T.resultMap[torrentInfoKey].(map[string]interface{})[torrentLengthKey]; ok {
return value.(int)
}
var total int
for _, file := range T.resultMap[torrentInfoKey].(map[string]interface{})[torrentFilesKey].([]interface{}) {
total += file.(map[string]interface{})[torrentLengthKey].(int)
}
return total
}
func (T *torrentDict) extractTrackerInfo() *TrackerInfo {
uniqueUrls := make(map[string]int)
currentCount := 0
if main, ok := T.resultMap[mainAnnounceKey]; ok {
if _, found := uniqueUrls[main.(string)]; !found {
uniqueUrls[main.(string)] = currentCount
currentCount++
}
}
if list, ok := T.resultMap[announceListKey]; ok {
for _, innerList := range list.([]interface{}) {
for _, item := range innerList.([]interface{}) {
if _, found := uniqueUrls[item.(string)]; !found {
uniqueUrls[item.(string)] = currentCount
currentCount++
}
}
}
}
trackerInfo := TrackerInfo{Urls: make([]string, len(uniqueUrls))}
for key, value := range uniqueUrls {
trackerInfo.Urls[value] = key
}
trackerInfo.Main = trackerInfo.Urls[0]
return &trackerInfo
}
//Decode accepts a byte slice and returns a map with information parsed.(panic if it fails) //Decode accepts a byte slice and returns a map with information parsed.(panic if it fails)
func Decode(data []byte) map[string]interface{} { func Decode(data []byte) map[string]interface{} {
result, _ := findParse(0, &data) result, _ := findParse(0, &data)
@ -76,7 +169,7 @@ func numberParse(startIdx int, data *[]byte) (result int, nextIdx int) {
func stringParse(startIdx int, data *[]byte) (result string, nextIdx int) { func stringParse(startIdx int, data *[]byte) (result string, nextIdx int) {
current := startIdx current := startIdx
for (*data)[current : current+1][0] != lengthValueStringSeparator { for (*data)[current : current+1][0] != lengthValueStringSeparatorToken {
current++ current++
} }
sizeStr, _ := strconv.Atoi(string(((*data)[startIdx:current]))) sizeStr, _ := strconv.Atoi(string(((*data)[startIdx:current])))

View file

@ -5,38 +5,7 @@ import (
"testing" "testing"
) )
func TestGenerateRandomPeerId(T *testing.T) {
T.Run("PeerIds are different", func(t *testing.T) {
keys := make(map[string]bool)
for i := 0; i < 10; i++ {
obj := NewQbitTorrent()
key := obj.PeerID()
t.Log(key)
if _, ok := keys[key]; ok {
t.Error("peerId must be random")
break
}
keys[key] = true
}
})
}
func TestGenerateRandomKey(T *testing.T) { func TestGenerateRandomKey(T *testing.T) {
T.Run("Keys are different", func(t *testing.T) {
keys := make(map[string]bool)
for i := 0; i < 10; i++ {
obj := NewQbitTorrent()
key := obj.Key()
t.Log(key)
if _, ok := keys[key]; ok {
t.Error("Keys must be random")
break
}
keys[key] = true
}
})
T.Run("Key has 8 length", func(t *testing.T) { T.Run("Key has 8 length", func(t *testing.T) {
obj := NewQbitTorrent() obj := NewQbitTorrent()
key := obj.Key() key := obj.Key()

View file

@ -38,7 +38,7 @@ func (R *ratioSpoofState) PrintState(exitedCH <-chan string) {
if R.retryAttempt > 0 { if R.retryAttempt > 0 {
retryStr = fmt.Sprintf("(*Retry %v - check your connection)", R.retryAttempt) retryStr = fmt.Sprintf("(*Retry %v - check your connection)", R.retryAttempt)
} }
fmt.Println(center(" RATIO-SPOOF ", width-len(" RATIO-SPOOF "), "#")) fmt.Printf("%s\n", center(" RATIO-SPOOF ", width-len(" RATIO-SPOOF "), "#"))
fmt.Printf(` fmt.Printf(`
Torrent: %v Torrent: %v
Tracker: %v Tracker: %v
@ -47,20 +47,15 @@ func (R *ratioSpoofState) PrintState(exitedCH <-chan string) {
Download Speed: %v/s Download Speed: %v/s
Upload Speed: %v/s Upload Speed: %v/s
Size: %v Size: %v
Emulation: %v | Port: %v`, R.torrentInfo.name, R.torrentInfo.trackerInfo.main, seedersStr, leechersStr, humanReadableSize(float64(R.input.downloadSpeed)), Emulation: %v | Port: %v`, R.torrentInfo.Name, R.torrentInfo.TrackerInfo.Main, seedersStr, leechersStr, humanReadableSize(float64(R.input.downloadSpeed)),
humanReadableSize(float64(R.input.uploadSpeed)), humanReadableSize(float64(R.torrentInfo.totalSize)), R.bitTorrentClient.Name(), R.input.port) humanReadableSize(float64(R.input.uploadSpeed)), humanReadableSize(float64(R.torrentInfo.TotalSize)), R.bitTorrentClient.Name(), R.input.port)
fmt.Println() fmt.Printf("\n\n%s\n\n", center(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF ", width-len(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF "), "#"))
fmt.Println()
fmt.Println(center(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF ", width-len(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF "), "#"))
fmt.Println()
for i := 0; i <= R.announceHistory.Len()-2; i++ { for i := 0; i <= R.announceHistory.Len()-2; i++ {
dequeItem := R.announceHistory.At(i).(announceEntry) dequeItem := R.announceHistory.At(i).(announceEntry)
fmt.Printf("#%v downloaded: %v(%.2f%%) | left: %v | uploaded: %v | announced", dequeItem.count, humanReadableSize(float64(dequeItem.downloaded)), dequeItem.percentDownloaded, humanReadableSize(float64(dequeItem.left)), humanReadableSize(float64(dequeItem.uploaded))) fmt.Printf("#%v downloaded: %v(%.2f%%) | left: %v | uploaded: %v | announced\n", dequeItem.count, humanReadableSize(float64(dequeItem.downloaded)), dequeItem.percentDownloaded, humanReadableSize(float64(dequeItem.left)), humanReadableSize(float64(dequeItem.uploaded)))
fmt.Println()
} }
lastDequeItem := R.announceHistory.At(R.announceHistory.Len() - 1).(announceEntry) lastDequeItem := R.announceHistory.At(R.announceHistory.Len() - 1).(announceEntry)
fmt.Printf("#%v downloaded: %v(%.2f%%) | left: %v | uploaded: %v | next announce in: %v %v", lastDequeItem.count, fmt.Printf("#%v downloaded: %v(%.2f%%) | left: %v | uploaded: %v | next announce in: %v %v\n", lastDequeItem.count,
humanReadableSize(float64(lastDequeItem.downloaded)), humanReadableSize(float64(lastDequeItem.downloaded)),
lastDequeItem.percentDownloaded, lastDequeItem.percentDownloaded,
humanReadableSize(float64(lastDequeItem.left)), humanReadableSize(float64(lastDequeItem.left)),
@ -69,14 +64,8 @@ func (R *ratioSpoofState) PrintState(exitedCH <-chan string) {
retryStr) retryStr)
if R.input.debug { if R.input.debug {
fmt.Println() fmt.Printf("\n%s\n", center(" DEBUG ", width-len(" DEBUG "), "#"))
fmt.Println() fmt.Printf("\n%s\n\n%s", R.lastAnounceRequest, R.lastTackerResponse)
fmt.Println(center(" DEBUG ", width-len(" DEBUG "), "#"))
fmt.Println()
fmt.Print(R.lastAnounceRequest)
fmt.Println()
fmt.Println()
fmt.Print(R.lastTackerResponse)
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }

View file

@ -3,14 +3,12 @@ package ratiospoof
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"crypto/sha1"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math" "math"
"math/rand" "math/rand"
"net/http" "net/http"
"net/url"
"os" "os"
"os/signal" "os/signal"
"strconv" "strconv"
@ -33,8 +31,9 @@ var validSpeedSufixes = [...]string{"kbps", "mbps"}
type ratioSpoofState struct { type ratioSpoofState struct {
mutex *sync.Mutex mutex *sync.Mutex
httpClient HttpClient httpClient HttpClient
torrentInfo *torrentInfo torrentInfo *beencode.TorrentInfo
input *inputParsed input *inputParsed
trackerState *httpTracker
bitTorrentClient TorrentClientEmulation bitTorrentClient TorrentClientEmulation
currentAnnounceTimer int currentAnnounceTimer int
announceInterval int announceInterval int
@ -48,6 +47,30 @@ type ratioSpoofState struct {
lastTackerResponse string lastTackerResponse string
retryAttempt int retryAttempt int
} }
type httpTracker struct {
urls []string
}
func newHttpTracker(torrentInfo *beencode.TorrentInfo) (*httpTracker, error) {
var result []string
for _, url := range torrentInfo.TrackerInfo.Urls {
if strings.HasPrefix(url, "http") {
result = append(result, url)
}
}
if len(result) == 0 {
return nil, errors.New("No tcp/http tracker url announce found")
}
return &httpTracker{urls: torrentInfo.TrackerInfo.Urls}, nil
}
func (T *httpTracker) SwapFirst(currentIdx int) {
aux := T.urls[0]
T.urls[0] = T.urls[currentIdx]
T.urls[currentIdx] = aux
}
type InputArgs struct { type InputArgs struct {
TorrentPath string TorrentPath string
InitialDownloaded string InitialDownloaded string
@ -68,34 +91,12 @@ type inputParsed struct {
debug bool debug bool
} }
type torrentInfo struct { func (I *InputArgs) parseInput(torrentInfo *beencode.TorrentInfo) (*inputParsed, error) {
name string downloaded, err := extractInputInitialByteCount(I.InitialDownloaded, torrentInfo.TotalSize, true)
pieceSize int
totalSize int
trackerInfo trackerInfo
infoHashURLEncoded string
}
func extractTorrentInfo(torrentPath string) (*torrentInfo, error) {
dat, err := ioutil.ReadFile(torrentPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
torrentMap := beencode.Decode(dat) uploaded, err := extractInputInitialByteCount(I.InitialUploaded, torrentInfo.TotalSize, false)
return &torrentInfo{
name: torrentMap["info"].(map[string]interface{})["name"].(string),
pieceSize: torrentMap["info"].(map[string]interface{})["piece length"].(int),
totalSize: extractTotalSize(torrentMap),
trackerInfo: extractTrackerInfo(torrentMap),
infoHashURLEncoded: extractInfoHashURLEncoded(dat, torrentMap),
}, nil
}
func (I *InputArgs) parseInput(torrentInfo *torrentInfo) (*inputParsed, error) {
downloaded, err := extractInputInitialByteCount(I.InitialDownloaded, torrentInfo.totalSize, true)
if err != nil {
return nil, err
}
uploaded, err := extractInputInitialByteCount(I.InitialUploaded, torrentInfo.totalSize, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -123,7 +124,17 @@ func (I *InputArgs) parseInput(torrentInfo *torrentInfo) (*inputParsed, error) {
func NewRatioSPoofState(input InputArgs, torrentClient TorrentClientEmulation, httpclient HttpClient) (*ratioSpoofState, error) { func NewRatioSPoofState(input InputArgs, torrentClient TorrentClientEmulation, httpclient HttpClient) (*ratioSpoofState, error) {
torrentInfo, err := extractTorrentInfo(input.TorrentPath) dat, err := ioutil.ReadFile(input.TorrentPath)
if err != nil {
return nil, err
}
torrentInfo, err := beencode.TorrentDictParse(dat)
if err != nil {
panic(err)
}
httpTracker, err := newHttpTracker(torrentInfo)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -137,6 +148,7 @@ func NewRatioSPoofState(input InputArgs, torrentClient TorrentClientEmulation, h
bitTorrentClient: torrentClient, bitTorrentClient: torrentClient,
httpClient: httpclient, httpClient: httpclient,
torrentInfo: torrentInfo, torrentInfo: torrentInfo,
trackerState: httpTracker,
input: inputParsed, input: inputParsed,
numWant: 200, numWant: 200,
status: "started", status: "started",
@ -183,17 +195,6 @@ func extractInputByteSpeed(initialSpeedInput string) (int, error) {
return ret, nil return ret, nil
} }
type trackerInfo struct {
main string
urls []string
}
func (T *trackerInfo) SwapFirst(currentIdx int) {
aux := T.urls[0]
T.urls[0] = T.urls[currentIdx]
T.urls[currentIdx] = aux
}
type trackerResponse struct { type trackerResponse struct {
minInterval int minInterval int
interval int interval int
@ -261,7 +262,7 @@ func (R *ratioSpoofState) Run() {
} }
func (R *ratioSpoofState) firstAnnounce() { func (R *ratioSpoofState) firstAnnounce() {
println("Trying to connect to the tracker...") println("Trying to connect to the tracker...")
R.addAnnounce(R.input.initialDownloaded, R.input.initialUploaded, calculateBytesLeft(R.input.initialDownloaded, R.torrentInfo.totalSize), (float32(R.input.initialDownloaded)/float32(R.torrentInfo.totalSize))*100) R.addAnnounce(R.input.initialDownloaded, R.input.initialUploaded, calculateBytesLeft(R.input.initialDownloaded, R.torrentInfo.TotalSize), (float32(R.input.initialDownloaded)/float32(R.torrentInfo.TotalSize))*100)
R.fireAnnounce(false) R.fireAnnounce(false)
} }
@ -283,7 +284,7 @@ func (R *ratioSpoofState) addAnnounce(currentDownloaded, currentUploaded, curren
} }
func (R *ratioSpoofState) fireAnnounce(retry bool) { func (R *ratioSpoofState) fireAnnounce(retry bool) {
lastAnnounce := R.announceHistory.Back().(announceEntry) lastAnnounce := R.announceHistory.Back().(announceEntry)
replacer := strings.NewReplacer("{infohash}", R.torrentInfo.infoHashURLEncoded, replacer := strings.NewReplacer("{infohash}", R.torrentInfo.InfoHashURLEncoded,
"{port}", fmt.Sprint(R.input.port), "{port}", fmt.Sprint(R.input.port),
"{peerid}", R.bitTorrentClient.PeerID(), "{peerid}", R.bitTorrentClient.PeerID(),
"{uploaded}", fmt.Sprint(lastAnnounce.uploaded), "{uploaded}", fmt.Sprint(lastAnnounce.uploaded),
@ -334,20 +335,20 @@ func (R *ratioSpoofState) generateNextAnnounce() {
currentDownloaded := lastAnnounce.downloaded currentDownloaded := lastAnnounce.downloaded
var downloadCandidate int var downloadCandidate int
if currentDownloaded < R.torrentInfo.totalSize { if currentDownloaded < R.torrentInfo.TotalSize {
downloadCandidate = calculateNextTotalSizeByte(R.input.downloadSpeed, currentDownloaded, R.torrentInfo.pieceSize, R.currentAnnounceTimer, R.torrentInfo.totalSize) downloadCandidate = calculateNextTotalSizeByte(R.input.downloadSpeed, currentDownloaded, R.torrentInfo.PieceSize, R.currentAnnounceTimer, R.torrentInfo.TotalSize)
} else { } else {
downloadCandidate = R.torrentInfo.totalSize downloadCandidate = R.torrentInfo.TotalSize
} }
currentUploaded := lastAnnounce.uploaded currentUploaded := lastAnnounce.uploaded
uploadCandidate := calculateNextTotalSizeByte(R.input.uploadSpeed, currentUploaded, R.torrentInfo.pieceSize, R.currentAnnounceTimer, 0) uploadCandidate := calculateNextTotalSizeByte(R.input.uploadSpeed, currentUploaded, R.torrentInfo.PieceSize, R.currentAnnounceTimer, 0)
leftCandidate := calculateBytesLeft(downloadCandidate, R.torrentInfo.totalSize) leftCandidate := calculateBytesLeft(downloadCandidate, R.torrentInfo.TotalSize)
d, u, l := R.bitTorrentClient.NextAmountReport(downloadCandidate, uploadCandidate, leftCandidate, R.torrentInfo.pieceSize) d, u, l := R.bitTorrentClient.NextAmountReport(downloadCandidate, uploadCandidate, leftCandidate, R.torrentInfo.PieceSize)
R.addAnnounce(d, u, l, (float32(d)/float32(R.torrentInfo.totalSize))*100) R.addAnnounce(d, u, l, (float32(d)/float32(R.torrentInfo.TotalSize))*100)
} }
func (R *ratioSpoofState) decreaseTimer() { func (R *ratioSpoofState) decreaseTimer() {
for { for {
@ -367,7 +368,7 @@ func (R *ratioSpoofState) changeCurrentTimer(newAnnounceRate int) {
} }
func (R *ratioSpoofState) tryMakeRequest(query string) *trackerResponse { func (R *ratioSpoofState) tryMakeRequest(query string) *trackerResponse {
for idx, url := range R.torrentInfo.trackerInfo.urls { for idx, url := range R.torrentInfo.TrackerInfo.Urls {
completeURL := url + "?" + strings.TrimLeft(query, "?") completeURL := url + "?" + strings.TrimLeft(query, "?")
R.lastAnounceRequest = completeURL R.lastAnounceRequest = completeURL
req, _ := http.NewRequest("GET", completeURL, nil) req, _ := http.NewRequest("GET", completeURL, nil)
@ -390,7 +391,7 @@ func (R *ratioSpoofState) tryMakeRequest(query string) *trackerResponse {
R.lastTackerResponse = string(bytesR) R.lastTackerResponse = string(bytesR)
decodedResp := beencode.Decode(bytesR) decodedResp := beencode.Decode(bytesR)
if idx != 0 { if idx != 0 {
R.torrentInfo.trackerInfo.SwapFirst(idx) R.trackerState.SwapFirst(idx)
} }
ret := extractTrackerResponse(decodedResp) ret := extractTrackerResponse(decodedResp)
return &ret return &ret
@ -416,60 +417,6 @@ func calculateNextTotalSizeByte(speedBytePerSecond, currentByte, pieceSizeByte,
return totalCandidate return totalCandidate
} }
func extractInfoHashURLEncoded(rawData []byte, torrentData map[string]interface{}) string {
byteOffsets := torrentData["info"].(map[string]interface{})["byte_offsets"].([]int)
h := sha1.New()
h.Write([]byte(rawData[byteOffsets[0]:byteOffsets[1]]))
ret := h.Sum(nil)
return url.QueryEscape(string(ret))
}
func extractTotalSize(torrentData map[string]interface{}) int {
if value, ok := torrentData["info"].(map[string]interface{})["length"]; ok {
return value.(int)
}
var total int
for _, file := range torrentData["info"].(map[string]interface{})["files"].([]interface{}) {
total += file.(map[string]interface{})["length"].(int)
}
return total
}
func extractTrackerInfo(torrentData map[string]interface{}) trackerInfo {
uniqueUrls := make(map[string]int)
currentCount := 0
if main, ok := torrentData["announce"]; ok && strings.HasPrefix(main.(string), "http") {
if _, found := uniqueUrls[main.(string)]; !found {
uniqueUrls[main.(string)] = currentCount
currentCount++
}
}
if list, ok := torrentData["announce-list"]; ok {
for _, innerList := range list.([]interface{}) {
for _, item := range innerList.([]interface{}) {
if _, found := uniqueUrls[item.(string)]; !found && strings.HasPrefix(item.(string), "http") {
uniqueUrls[item.(string)] = currentCount
currentCount++
}
}
}
}
trackerInfo := trackerInfo{urls: make([]string, len(uniqueUrls))}
for key, value := range uniqueUrls {
trackerInfo.urls[value] = key
}
trackerInfo.main = trackerInfo.urls[0]
if len(trackerInfo.urls) == 0 {
panic("No tcp/http tracker url announce found'")
}
return trackerInfo
}
func extractTrackerResponse(datatrackerResponse map[string]interface{}) trackerResponse { func extractTrackerResponse(datatrackerResponse map[string]interface{}) trackerResponse {
var result trackerResponse var result trackerResponse
if v, ok := datatrackerResponse["failure reason"].(string); ok && len(v) > 0 { if v, ok := datatrackerResponse["failure reason"].(string); ok && len(v) > 0 {