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
import (
"crypto/sha1"
"net/url"
"strconv"
)
const (
dictToken = byte('d')
numberToken = byte('i')
listToken = byte('l')
endOfCollectionToken = byte('e')
lengthValueStringSeparator = byte(':')
dictToken = byte('d')
numberToken = byte('i')
listToken = byte('l')
endOfCollectionToken = byte('e')
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)
func Decode(data []byte) map[string]interface{} {
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) {
current := startIdx
for (*data)[current : current+1][0] != lengthValueStringSeparator {
for (*data)[current : current+1][0] != lengthValueStringSeparatorToken {
current++
}
sizeStr, _ := strconv.Atoi(string(((*data)[startIdx:current])))

View file

@ -5,38 +5,7 @@ import (
"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) {
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) {
obj := NewQbitTorrent()
key := obj.Key()

View file

@ -38,7 +38,7 @@ func (R *ratioSpoofState) PrintState(exitedCH <-chan string) {
if R.retryAttempt > 0 {
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(`
Torrent: %v
Tracker: %v
@ -47,20 +47,15 @@ func (R *ratioSpoofState) PrintState(exitedCH <-chan string) {
Download Speed: %v/s
Upload Speed: %v/s
Size: %v
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)
fmt.Println()
fmt.Println()
fmt.Println(center(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF ", width-len(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF "), "#"))
fmt.Println()
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)
fmt.Printf("\n\n%s\n\n", center(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF ", width-len(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF "), "#"))
for i := 0; i <= R.announceHistory.Len()-2; i++ {
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.Println()
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)))
}
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)),
lastDequeItem.percentDownloaded,
humanReadableSize(float64(lastDequeItem.left)),
@ -69,14 +64,8 @@ func (R *ratioSpoofState) PrintState(exitedCH <-chan string) {
retryStr)
if R.input.debug {
fmt.Println()
fmt.Println()
fmt.Println(center(" DEBUG ", width-len(" DEBUG "), "#"))
fmt.Println()
fmt.Print(R.lastAnounceRequest)
fmt.Println()
fmt.Println()
fmt.Print(R.lastTackerResponse)
fmt.Printf("\n%s\n", center(" DEBUG ", width-len(" DEBUG "), "#"))
fmt.Printf("\n%s\n\n%s", R.lastAnounceRequest, R.lastTackerResponse)
}
time.Sleep(1 * time.Second)
}

View file

@ -3,14 +3,12 @@ package ratiospoof
import (
"bytes"
"compress/gzip"
"crypto/sha1"
"errors"
"fmt"
"io/ioutil"
"math"
"math/rand"
"net/http"
"net/url"
"os"
"os/signal"
"strconv"
@ -33,8 +31,9 @@ var validSpeedSufixes = [...]string{"kbps", "mbps"}
type ratioSpoofState struct {
mutex *sync.Mutex
httpClient HttpClient
torrentInfo *torrentInfo
torrentInfo *beencode.TorrentInfo
input *inputParsed
trackerState *httpTracker
bitTorrentClient TorrentClientEmulation
currentAnnounceTimer int
announceInterval int
@ -48,6 +47,30 @@ type ratioSpoofState struct {
lastTackerResponse string
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 {
TorrentPath string
InitialDownloaded string
@ -68,34 +91,12 @@ type inputParsed struct {
debug bool
}
type torrentInfo struct {
name string
pieceSize int
totalSize int
trackerInfo trackerInfo
infoHashURLEncoded string
}
func extractTorrentInfo(torrentPath string) (*torrentInfo, error) {
dat, err := ioutil.ReadFile(torrentPath)
func (I *InputArgs) parseInput(torrentInfo *beencode.TorrentInfo) (*inputParsed, error) {
downloaded, err := extractInputInitialByteCount(I.InitialDownloaded, torrentInfo.TotalSize, true)
if err != nil {
return nil, err
}
torrentMap := beencode.Decode(dat)
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)
uploaded, err := extractInputInitialByteCount(I.InitialUploaded, torrentInfo.TotalSize, false)
if err != nil {
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) {
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 {
panic(err)
}
@ -137,6 +148,7 @@ func NewRatioSPoofState(input InputArgs, torrentClient TorrentClientEmulation, h
bitTorrentClient: torrentClient,
httpClient: httpclient,
torrentInfo: torrentInfo,
trackerState: httpTracker,
input: inputParsed,
numWant: 200,
status: "started",
@ -183,17 +195,6 @@ func extractInputByteSpeed(initialSpeedInput string) (int, error) {
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 {
minInterval int
interval int
@ -261,7 +262,7 @@ func (R *ratioSpoofState) Run() {
}
func (R *ratioSpoofState) firstAnnounce() {
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)
}
@ -283,7 +284,7 @@ func (R *ratioSpoofState) addAnnounce(currentDownloaded, currentUploaded, curren
}
func (R *ratioSpoofState) fireAnnounce(retry bool) {
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),
"{peerid}", R.bitTorrentClient.PeerID(),
"{uploaded}", fmt.Sprint(lastAnnounce.uploaded),
@ -334,20 +335,20 @@ func (R *ratioSpoofState) generateNextAnnounce() {
currentDownloaded := lastAnnounce.downloaded
var downloadCandidate int
if currentDownloaded < R.torrentInfo.totalSize {
downloadCandidate = calculateNextTotalSizeByte(R.input.downloadSpeed, currentDownloaded, R.torrentInfo.pieceSize, R.currentAnnounceTimer, R.torrentInfo.totalSize)
if currentDownloaded < R.torrentInfo.TotalSize {
downloadCandidate = calculateNextTotalSizeByte(R.input.downloadSpeed, currentDownloaded, R.torrentInfo.PieceSize, R.currentAnnounceTimer, R.torrentInfo.TotalSize)
} else {
downloadCandidate = R.torrentInfo.totalSize
downloadCandidate = R.torrentInfo.TotalSize
}
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() {
for {
@ -367,7 +368,7 @@ func (R *ratioSpoofState) changeCurrentTimer(newAnnounceRate int) {
}
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, "?")
R.lastAnounceRequest = completeURL
req, _ := http.NewRequest("GET", completeURL, nil)
@ -390,7 +391,7 @@ func (R *ratioSpoofState) tryMakeRequest(query string) *trackerResponse {
R.lastTackerResponse = string(bytesR)
decodedResp := beencode.Decode(bytesR)
if idx != 0 {
R.torrentInfo.trackerInfo.SwapFirst(idx)
R.trackerState.SwapFirst(idx)
}
ret := extractTrackerResponse(decodedResp)
return &ret
@ -416,60 +417,6 @@ func calculateNextTotalSizeByte(speedBytePerSecond, currentByte, pieceSizeByte,
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 {
var result trackerResponse
if v, ok := datatrackerResponse["failure reason"].(string); ok && len(v) > 0 {