initial multiple-client build

This commit is contained in:
ap-pauloafonso 2021-03-12 23:15:18 -03:00
parent 0dc1a674c7
commit c1a86a8091
18 changed files with 354 additions and 150 deletions

View file

@ -1,17 +0,0 @@
#!/bin/sh
version=$1
if [ -z "$version" ]; then
echo "usage: $0 <version>"
exit 1
fi
rm -rf ./out
env GOOS=darwin GOARCH=amd64 go build -v -o ./out/mac/ratio-spoof github.com/ap-pauloafonso/ratio-spoof/cmd
env GOOS=linux GOARCH=amd64 go build -v -o ./out/linux/ratio-spoof github.com/ap-pauloafonso/ratio-spoof/cmd
env GOOS=windows GOARCH=amd64 go build -v -o ./out/windows/ratio-spoof.exe github.com/ap-pauloafonso/ratio-spoof/cmd
cd out/
zip ratio-spoof-$version\(linux-mac-windows\).zip -r .

View file

@ -6,7 +6,6 @@ import (
"log" "log"
"os" "os"
"github.com/ap-pauloafonso/ratio-spoof/internal/emulation"
"github.com/ap-pauloafonso/ratio-spoof/internal/input" "github.com/ap-pauloafonso/ratio-spoof/internal/input"
"github.com/ap-pauloafonso/ratio-spoof/internal/printer" "github.com/ap-pauloafonso/ratio-spoof/internal/printer"
"github.com/ap-pauloafonso/ratio-spoof/internal/ratiospoof" "github.com/ap-pauloafonso/ratio-spoof/internal/ratiospoof"
@ -24,13 +23,15 @@ func main() {
//optional //optional
port := flag.Int("p", 8999, "a PORT") port := flag.Int("p", 8999, "a PORT")
debug := flag.Bool("debug", false, "") debug := flag.Bool("debug", false, "")
client := flag.String("c", "qbit-4.0.3", "emulated client")
flag.Usage = func() { flag.Usage = func() {
fmt.Printf("usage: %s -t <TORRENT_PATH> -d <INITIAL_DOWNLOADED> -ds <DOWNLOAD_SPEED> -u <INITIAL_UPLOADED> -us <UPLOAD_SPEED>\n", os.Args[0]) fmt.Printf("usage: %s -t <TORRENT_PATH> -d <INITIAL_DOWNLOADED> -ds <DOWNLOAD_SPEED> -u <INITIAL_UPLOADED> -us <UPLOAD_SPEED>\n", os.Args[0])
fmt.Print(` fmt.Print(`
optional arguments: optional arguments:
-h show this help message and exit -h show this help message and exit
-p [PORT] change the port number, the default is 8999 -p [PORT] change the port number, default: 8999
-c [CLIENT_CODE] change the client emulation, default: qbit-4.0.3
required arguments: required arguments:
-t <TORRENT_PATH> -t <TORRENT_PATH>
@ -41,6 +42,7 @@ required arguments:
<INITIAL_DOWNLOADED> and <INITIAL_UPLOADED> must be in %, b, kb, mb, gb, tb <INITIAL_DOWNLOADED> and <INITIAL_UPLOADED> must be in %, b, kb, mb, gb, tb
<DOWNLOAD_SPEED> and <UPLOAD_SPEED> must be in kbps, mbps <DOWNLOAD_SPEED> and <UPLOAD_SPEED> must be in kbps, mbps
[CLIENT_CODE] options: qbit-4.0.3, qbit-4.3.2
`) `)
} }
@ -51,7 +53,6 @@ required arguments:
return return
} }
qbit := emulation.NewQbitTorrent()
r, err := ratiospoof.NewRatioSpoofState( r, err := ratiospoof.NewRatioSpoofState(
input.InputArgs{ input.InputArgs{
TorrentPath: *torrentPath, TorrentPath: *torrentPath,
@ -61,8 +62,8 @@ required arguments:
UploadSpeed: *uploadSpeed, UploadSpeed: *uploadSpeed,
Port: *port, Port: *port,
Debug: *debug, Debug: *debug,
}, Client: *client,
qbit) })
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)

6
go.mod
View file

@ -1,9 +1,13 @@
module github.com/ap-pauloafonso/ratio-spoof module github.com/ap-pauloafonso/ratio-spoof
go 1.15 go 1.16
require ( require (
github.com/gammazero/deque v0.0.0-20201010052221-3932da5530cc github.com/gammazero/deque v0.0.0-20201010052221-3932da5530cc
github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4 // indirect
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
) )

27
go.sum
View file

@ -1,6 +1,33 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gammazero/deque v0.0.0-20201010052221-3932da5530cc h1:F7BbnLACph7UYiz9ZHi6npcROwKaZUyviDjsNERsoMM= github.com/gammazero/deque v0.0.0-20201010052221-3932da5530cc h1:F7BbnLACph7UYiz9ZHi6npcROwKaZUyviDjsNERsoMM=
github.com/gammazero/deque v0.0.0-20201010052221-3932da5530cc/go.mod h1:IlBLfYXnuw9sspy1XS6ctu5exGb6WHGKQsyo4s7bOEA= github.com/gammazero/deque v0.0.0-20201010052221-3932da5530cc/go.mod h1:IlBLfYXnuw9sspy1XS6ctu5exGb6WHGKQsyo4s7bOEA=
github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4 h1:OL2d27ueTKnlQJoqLW2fc9pWYulFnJYLWzomGV7HqZo=
github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4/go.mod h1:Pw1H1OjSNHiqeuxAduB1BKYXIwFtsyrY47nEqSgEiCM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea h1:CyhwejzVGvZ3Q2PSbQ4NRRYn+ZWv5eS1vlaEusT+bAI=
github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea/go.mod h1:eNr558nEUjP8acGw8FFjTeWvSgU1stO7FAO6eknhHe4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,84 @@
package emulation
import (
"embed"
"encoding/json"
"io/ioutil"
"github.com/ap-pauloafonso/ratio-spoof/internal/generator"
)
type ClientInfo struct {
Name string `json:"name"`
PeerID struct {
Generator string `json:"generator"`
Regex string `json:"regex"`
} `json:"peerId"`
Key struct {
Generator string `json:"generator"`
Regex string `json:"regex"`
} `json:"key"`
Rounding struct {
Generator string `json:"generator"`
Regex string `json:"regex"`
} `json:"rounding"`
Query string `json:"query"`
Headers map[string]string `json:"headers"`
}
type Emulation struct {
PeerIdGenerator generator.PeerIdGenerator
KeyGenerator generator.KeyGenerator
Query string
Name string
Headers map[string]string
RoudingGenerator generator.RoundingGenerator
}
func NewEmulation(code string) (*Emulation, error) {
c, err := extractClient(code)
if err != nil {
return nil, err
}
peerG, err := generator.NewPeerIdGenerator(c.PeerID.Generator, c.PeerID.Regex)
if err != nil {
return nil, err
}
keyG, err := generator.NewKeyGenerator(c.Key.Generator)
if err != nil {
return nil, err
}
roudingG, err := generator.NewRoundingGenerator(c.Rounding.Generator)
if err != nil {
return nil, err
}
return &Emulation{PeerIdGenerator: peerG, KeyGenerator: keyG, RoudingGenerator: roudingG,
Headers: c.Headers, Name: c.Name, Query: c.Query}, nil
}
//go:embed static
var staticFiles embed.FS
func extractClient(code string) (*ClientInfo, error) {
f, err := staticFiles.Open("static/" + code + ".json")
if err != nil {
return nil, err
}
bytes, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
var client ClientInfo
json.Unmarshal(bytes, &client)
return &client, nil
}

View file

@ -0,0 +1,74 @@
package emulation
import (
"io/fs"
"strings"
"testing"
)
func TestNewEmulation(t *testing.T) {
var counter int
fs.WalkDir(staticFiles, ".", func(path string, d fs.DirEntry, err error) error {
if counter > 1 {
code := strings.TrimRight(strings.TrimLeft(path, "static/"), ".json")
e, err := NewEmulation(code)
if err != nil {
t.Error("should not return error ")
}
peerId := e.PeerIdGenerator.PeerId()
key := e.KeyGenerator.Key()
d, u, l := e.RoudingGenerator.NextAmountReport(2*1024*1024*1024, 1024*1024*1024, 3*1024*1024*1024, 1024)
if peerId == "" {
t.Errorf("%s.json should be able to generate PeerId", code)
}
if key == "" {
t.Errorf("%s.json should be able to generate Key", code)
}
if d <= 0 || u <= 0 || l <= 0 {
t.Errorf("%s.json should be able to round candidates", code)
}
}
counter++
return nil
})
}
func TestExtractClient(t *testing.T) {
var counter int
fs.WalkDir(staticFiles, ".", func(path string, d fs.DirEntry, err error) error {
if counter > 1 {
code := strings.TrimRight(strings.TrimLeft(path, "static/"), ".json")
c, e := extractClient(code)
if e != nil || err != nil {
t.Error("should not return error")
}
if c.Key.Generator == "" && c.Key.Regex == "" {
t.Errorf("%s.json should have key generator properties", code)
}
if c.PeerID.Generator == "" && c.PeerID.Regex == "" {
t.Errorf("%s.json should have PeerId generator properties", code)
}
if c.Rounding.Generator == "" && c.Rounding.Regex == "" {
t.Errorf("%s.json should have rouding generator properties", code)
}
if c.Name == "" {
t.Errorf("%s.json should have a name", code)
}
if c.Query == "" {
t.Errorf("%s.json should have a query", code)
}
if len(c.Headers) == 0 {
t.Errorf("%s.json should have headers", code)
}
}
counter++
return nil
})
}

View file

@ -1,83 +0,0 @@
package emulation
import (
"encoding/hex"
"math/rand"
"strings"
"time"
)
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
name = "qBittorrent v4.03"
query = "info_hash={infohash}&peer_id={peerid}&port={port}&uploaded={uploaded}&downloaded={downloaded}&left={left}&corrupt=0&key={key}&event={event}&numwant={numwant}&compact=1&no_peer_id=1&supportcrypto=1&redundant=0"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type qbitTorrent struct {
name string
query string
dictHeaders map[string]string
key string
peerID string
}
func NewQbitTorrent() *qbitTorrent {
return &qbitTorrent{
name: name,
query: query,
dictHeaders: generateHeaders(),
key: generateKey(),
peerID: generatePeerID(),
}
}
func (qb *qbitTorrent) Name() string {
return qb.name
}
func (qb *qbitTorrent) PeerID() string {
return qb.peerID
}
func (qb *qbitTorrent) Key() string {
return qb.key
}
func (qb *qbitTorrent) Query() string {
return query
}
func (qb *qbitTorrent) Headers() map[string]string {
return qb.dictHeaders
}
func (qb *qbitTorrent) NextAmountReport(downloadCandidateNextAmount, uploadCandidateNextAmount, leftCandidateNextAmount, pieceSize int) (downloaded, uploaded, left int) {
d := downloadCandidateNextAmount
u := uploadCandidateNextAmount - (uploadCandidateNextAmount % (16 * 1024))
l := leftCandidateNextAmount - (leftCandidateNextAmount % pieceSize)
return d, u, l
}
func generateHeaders() map[string]string {
return map[string]string{"User-Agent": "qBittorrent/4.0.3", "Accept-Encoding": "gzip"}
}
func generatePeerID() string {
return "-qB4030-" + randStringBytes(12)
}
func randStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func generateKey() string {
randomBytes := make([]byte, 4)
rand.Read(randomBytes)
str := hex.EncodeToString(randomBytes)
return strings.ToUpper(str)
}

View file

@ -1,26 +0,0 @@
package emulation
import (
"strings"
"testing"
)
func TestGenerateRandomKey(T *testing.T) {
T.Run("Key has 8 length", func(t *testing.T) {
obj := NewQbitTorrent()
key := obj.Key()
if len(key) != 8 {
t.Error("Keys must have length of 8")
}
})
T.Run("Key must be uppercase", func(t *testing.T) {
obj := NewQbitTorrent()
key := obj.Key()
if strings.ToUpper(key) != key {
t.Error("Keys must be uppercase")
}
})
}

View file

@ -0,0 +1,17 @@
{
"name":"qBittorrent v4.0.3",
"peerId":{
"regex":"-qB4030-[A-Za-z0-9_~\\(\\)\\!\\.\\*-]{12}"
},
"key": {
"generator":"defaultKeyGenerator"
},
"rounding": {
"generator":"defaultRoudingGenerator"
},
"query":"info_hash={infohash}&peer_id={peerid}&port={port}&uploaded={uploaded}&downloaded={downloaded}&left={left}&corrupt=0&key={key}&event={event}&numwant={numwant}&compact=1&no_peer_id=1&supportcrypto=1&redundant=0",
"headers":{
"User-Agent" :"qBittorrent/4.0.3",
"Accept-Encoding": "gzip"
}
}

View file

@ -0,0 +1,17 @@
{
"name":"qBittorrent v4.3.2",
"peerId":{
"regex":"-qB4320-[A-Za-z0-9_~\\(\\)\\!\\.\\*-]{12}"
},
"key": {
"generator":"defaultKeyGenerator"
},
"rounding": {
"generator":"defaultRoudingGenerator"
},
"query":"info_hash={infohash}&peer_id={peerid}&port={port}&uploaded={uploaded}&downloaded={downloaded}&left={left}&corrupt=0&key={key}&event={event}&numwant={numwant}&compact=1&no_peer_id=1&supportcrypto=1&redundant=0",
"headers":{
"User-Agent" :"qBittorrent/4.3.2",
"Accept-Encoding": "gzip"
}
}

27
internal/generator/key.go Normal file
View file

@ -0,0 +1,27 @@
package generator
import (
"crypto/rand"
"encoding/hex"
"strings"
)
type KeyGenerator interface {
Key() string
}
func NewKeyGenerator(generatorCode string) (KeyGenerator, error) {
randomBytes := make([]byte, 4)
rand.Read(randomBytes)
str := hex.EncodeToString(randomBytes)
result := strings.ToUpper(str)
return &DefaultKeyGenerator{generated: result}, nil
}
type DefaultKeyGenerator struct {
generated string
}
func (d *DefaultKeyGenerator) Key() string {
return d.generated
}

View file

@ -0,0 +1,25 @@
package generator
import (
regen "github.com/zach-klippenstein/goregen"
)
type PeerIdGenerator interface {
PeerId() string
}
type RegexPeerIdGenerator struct {
generated string
}
func NewPeerIdGenerator(generatorCode, pattern string) (PeerIdGenerator, error) {
result, err := regen.Generate(pattern)
if err != nil {
return nil, err
}
return &RegexPeerIdGenerator{generated: result}, nil
}
func (d *RegexPeerIdGenerator) PeerId() string {
return d.generated
}

View file

@ -0,0 +1,20 @@
package generator
type RoundingGenerator interface {
NextAmountReport(downloadCandidateNextAmount, uploadCandidateNextAmount, leftCandidateNextAmount, pieceSize int) (downloaded, uploaded, left int)
}
func NewRoundingGenerator(code string) (RoundingGenerator, error) {
return &DefaultRoundingGenerator{}, nil
}
type DefaultRoundingGenerator struct{}
func (d *DefaultRoundingGenerator) NextAmountReport(downloadCandidateNextAmount, uploadCandidateNextAmount, leftCandidateNextAmount, pieceSize int) (downloaded, uploaded, left int) {
down := downloadCandidateNextAmount
up := uploadCandidateNextAmount - (uploadCandidateNextAmount % (16 * 1024))
l := leftCandidateNextAmount - (leftCandidateNextAmount % pieceSize)
return down, up, l
}

View file

@ -0,0 +1,21 @@
package generator
import "testing"
func TestNextAmountReport(t *testing.T) {
r, _ := NewRoundingGenerator("")
d, u, l := r.NextAmountReport(656497856, 46479878, 7879879, 1024)
//same
if d != 656497856 {
t.Errorf("[download]got %v want %v", d, 656497856)
}
//16kb round
if u != 46465024 {
t.Errorf("[upload]got %v want %v", u, 46465024)
}
//piece size round
if l != 7879680 {
t.Errorf("[left]got %v want %v", l, 7879680)
}
}

View file

@ -21,6 +21,7 @@ type InputArgs struct {
InitialDownloaded string InitialDownloaded string
DownloadSpeed string DownloadSpeed string
InitialUploaded string InitialUploaded string
Client string
UploadSpeed string UploadSpeed string
Port int Port int
Debug bool Debug bool

View file

@ -55,7 +55,7 @@ func PrintState(state *ratiospoof.RatioSpoof) {
Upload Speed: %v/s Upload Speed: %v/s
Size: %v Size: %v
Emulation: %v | Port: %v`, state.TorrentInfo.Name, state.TorrentInfo.TrackerInfo.Main, seedersStr, leechersStr, humanReadableSize(float64(state.Input.DownloadSpeed)), Emulation: %v | Port: %v`, state.TorrentInfo.Name, state.TorrentInfo.TrackerInfo.Main, seedersStr, leechersStr, humanReadableSize(float64(state.Input.DownloadSpeed)),
humanReadableSize(float64(state.Input.UploadSpeed)), humanReadableSize(float64(state.TorrentInfo.TotalSize)), state.BitTorrentClient.Name(), state.Input.Port) humanReadableSize(float64(state.Input.UploadSpeed)), humanReadableSize(float64(state.TorrentInfo.TotalSize)), state.BitTorrentClient.Name, state.Input.Port)
fmt.Printf("\n\n%s\n\n", center(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF ", width-len(" GITHUB.COM/AP-PAULOAFONSO/RATIO-SPOOF "), "#")) 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 <= state.AnnounceHistory.Len()-2; i++ { for i := 0; i <= state.AnnounceHistory.Len()-2; i++ {
dequeItem := state.AnnounceHistory.At(i).(ratiospoof.AnnounceEntry) dequeItem := state.AnnounceHistory.At(i).(ratiospoof.AnnounceEntry)

View file

@ -14,6 +14,7 @@ import (
"time" "time"
"github.com/ap-pauloafonso/ratio-spoof/internal/bencode" "github.com/ap-pauloafonso/ratio-spoof/internal/bencode"
"github.com/ap-pauloafonso/ratio-spoof/internal/emulation"
"github.com/ap-pauloafonso/ratio-spoof/internal/input" "github.com/ap-pauloafonso/ratio-spoof/internal/input"
"github.com/ap-pauloafonso/ratio-spoof/internal/tracker" "github.com/ap-pauloafonso/ratio-spoof/internal/tracker"
"github.com/gammazero/deque" "github.com/gammazero/deque"
@ -28,7 +29,7 @@ type RatioSpoof struct {
TorrentInfo *bencode.TorrentInfo TorrentInfo *bencode.TorrentInfo
Input *input.InputParsed Input *input.InputParsed
Tracker *tracker.HttpTracker Tracker *tracker.HttpTracker
BitTorrentClient TorrentClientEmulation BitTorrentClient *emulation.Emulation
AnnounceInterval int AnnounceInterval int
EstimatedTimeToAnnounce time.Time EstimatedTimeToAnnounce time.Time
EstimatedTimeToAnnounceUpdateCh chan int EstimatedTimeToAnnounceUpdateCh chan int
@ -41,15 +42,6 @@ type RatioSpoof struct {
StopPrintCH chan interface{} StopPrintCH chan interface{}
} }
type TorrentClientEmulation interface {
PeerID() string
Key() string
Query() string
Name() string
Headers() map[string]string
NextAmountReport(DownloadCandidateNextAmount, UploadCandidateNextAmount, leftCandidateNextAmount, pieceSize int) (downloaded, uploaded, left int)
}
type AnnounceEntry struct { type AnnounceEntry struct {
Count int Count int
Downloaded int Downloaded int
@ -62,7 +54,7 @@ type announceHistory struct {
deque.Deque deque.Deque
} }
func NewRatioSpoofState(input input.InputArgs, torrentClient TorrentClientEmulation) (*RatioSpoof, error) { func NewRatioSpoofState(input input.InputArgs) (*RatioSpoof, error) {
EstimatedTimeToAnnounceUpdateCh := make(chan int) EstimatedTimeToAnnounceUpdateCh := make(chan int)
stopPrintCh := make(chan interface{}) stopPrintCh := make(chan interface{})
dat, err := ioutil.ReadFile(input.TorrentPath) dat, err := ioutil.ReadFile(input.TorrentPath)
@ -70,6 +62,11 @@ func NewRatioSpoofState(input input.InputArgs, torrentClient TorrentClientEmulat
return nil, err return nil, err
} }
client, err := emulation.NewEmulation(input.Client)
if err != nil {
return nil, errors.New("Error building the emulated client with the code")
}
torrentInfo, err := bencode.TorrentDictParse(dat) torrentInfo, err := bencode.TorrentDictParse(dat)
if err != nil { if err != nil {
return nil, errors.New("failed to parse the torrent file") return nil, errors.New("failed to parse the torrent file")
@ -86,7 +83,7 @@ func NewRatioSpoofState(input input.InputArgs, torrentClient TorrentClientEmulat
} }
return &RatioSpoof{ return &RatioSpoof{
BitTorrentClient: torrentClient, BitTorrentClient: client,
TorrentInfo: torrentInfo, TorrentInfo: torrentInfo,
Tracker: httpTracker, Tracker: httpTracker,
Input: inputParsed, Input: inputParsed,
@ -171,15 +168,15 @@ func (R *RatioSpoof) fireAnnounce(retry bool) error {
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.PeerIdGenerator.PeerId(),
"{uploaded}", fmt.Sprint(lastAnnounce.Uploaded), "{uploaded}", fmt.Sprint(lastAnnounce.Uploaded),
"{downloaded}", fmt.Sprint(lastAnnounce.Downloaded), "{downloaded}", fmt.Sprint(lastAnnounce.Downloaded),
"{left}", fmt.Sprint(lastAnnounce.Left), "{left}", fmt.Sprint(lastAnnounce.Left),
"{key}", R.BitTorrentClient.Key(), "{key}", R.BitTorrentClient.KeyGenerator.Key(),
"{event}", R.Status, "{event}", R.Status,
"{numwant}", fmt.Sprint(R.NumWant)) "{numwant}", fmt.Sprint(R.NumWant))
query := replacer.Replace(R.BitTorrentClient.Query()) query := replacer.Replace(R.BitTorrentClient.Query)
trackerResp, err := R.Tracker.Announce(query, R.BitTorrentClient.Headers(), retry, R.EstimatedTimeToAnnounceUpdateCh) trackerResp, err := R.Tracker.Announce(query, R.BitTorrentClient.Headers, retry, R.EstimatedTimeToAnnounceUpdateCh)
if err != nil { if err != nil {
log.Fatalf("failed to reach the tracker:\n%s ", err.Error()) log.Fatalf("failed to reach the tracker:\n%s ", err.Error())
} }
@ -206,7 +203,7 @@ func (R *RatioSpoof) generateNextAnnounce() {
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.RoudingGenerator.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)
} }

15
makefile Normal file
View file

@ -0,0 +1,15 @@
test:
go test ./... --cover
torrent-test:
go run cmd/main.go -c qbit-4.3.2 -t internal/bencode/torrent_files_test/Fedora-Workstation-Live-x86_64-33.torrent -d 0% -ds 100kbps -u 0% -us 100kbps -debug
release:
@if test -z "$(rsversion)"; then echo "usage: make release rsversion=v1.2"; exit 1; fi
rm -rf ./out
env GOOS=darwin GOARCH=amd64 go build -v -o ./out/mac/ratio-spoof github.com/ap-pauloafonso/ratio-spoof/cmd
env GOOS=linux GOARCH=amd64 go build -v -o ./out/linux/ratio-spoof github.com/ap-pauloafonso/ratio-spoof/cmd
env GOOS=windows GOARCH=amd64 go build -v -o ./out/windows/ratio-spoof.exe github.com/ap-pauloafonso/ratio-spoof/cmd
cd out/ ; zip ratio-spoof-$(rsversion)\(linux-mac-windows\).zip -r .