130 lines
2.5 KiB
Go
130 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/anacrolix/tagflag"
|
|
"github.com/anacrolix/torrent"
|
|
"github.com/anacrolix/torrent/metainfo"
|
|
"github.com/anacrolix/torrent/tracker"
|
|
"github.com/davecgh/go-spew/spew"
|
|
)
|
|
|
|
func argSpec(arg string) (ts *torrent.TorrentSpec, _ error) {
|
|
if strings.HasPrefix(arg, "magnet:") {
|
|
return torrent.TorrentSpecFromMagnetUri(arg)
|
|
}
|
|
mi, fileErr := metainfo.LoadFromFile(arg)
|
|
if fileErr == nil {
|
|
ts = torrent.TorrentSpecFromMetaInfo(mi)
|
|
return
|
|
}
|
|
var ih torrent.InfoHash
|
|
ihErr := ih.FromHexString(arg)
|
|
if ihErr == nil {
|
|
ts = &torrent.TorrentSpec{
|
|
InfoHash: ih,
|
|
}
|
|
return
|
|
}
|
|
if len(arg) == 40 {
|
|
return nil, ihErr
|
|
} else {
|
|
return nil, fileErr
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
flags := struct {
|
|
Port uint16
|
|
Tracker []string
|
|
tagflag.StartPos
|
|
Torrents []string `arity:"+"`
|
|
}{
|
|
Port: 50007,
|
|
}
|
|
tagflag.Parse(&flags)
|
|
var exitCode int32
|
|
var wg sync.WaitGroup
|
|
startAnnounce := func(ih torrent.InfoHash, tURI string) {
|
|
ar := tracker.AnnounceRequest{
|
|
NumWant: -1,
|
|
Left: -1,
|
|
Port: flags.Port,
|
|
InfoHash: ih,
|
|
}
|
|
wg.Add(1)
|
|
go func(tURI string) {
|
|
defer wg.Done()
|
|
if doTracker(tURI, ar) {
|
|
atomic.StoreInt32(&exitCode, 1)
|
|
}
|
|
}(tURI)
|
|
}
|
|
for _, arg := range flags.Torrents {
|
|
ts, err := argSpec(arg)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
for _, tier := range ts.Trackers {
|
|
for _, tURI := range tier {
|
|
startAnnounce(ts.InfoHash, tURI)
|
|
}
|
|
}
|
|
for _, tUri := range flags.Tracker {
|
|
startAnnounce(ts.InfoHash, tUri)
|
|
}
|
|
}
|
|
wg.Wait()
|
|
os.Exit(int(exitCode))
|
|
}
|
|
|
|
func doTracker(tURI string, ar tracker.AnnounceRequest) (hadError bool) {
|
|
for _, res := range announces(tURI, ar) {
|
|
err := res.error
|
|
resp := res.AnnounceResponse
|
|
if err != nil {
|
|
hadError = true
|
|
log.Printf("error announcing to %q: %s", tURI, err)
|
|
continue
|
|
}
|
|
fmt.Printf("from %q for %x:\n%s", tURI, ar.InfoHash, spew.Sdump(resp))
|
|
}
|
|
return
|
|
}
|
|
|
|
type announceResult struct {
|
|
tracker.AnnounceResponse
|
|
error
|
|
}
|
|
|
|
func announces(uri string, ar tracker.AnnounceRequest) (ret []announceResult) {
|
|
u, err := url.Parse(uri)
|
|
if err != nil {
|
|
return []announceResult{{error: err}}
|
|
}
|
|
a := tracker.Announce{
|
|
Request: ar,
|
|
TrackerUrl: uri,
|
|
}
|
|
if u.Scheme == "udp" {
|
|
a.UdpNetwork = "udp4"
|
|
ret = append(ret, announce(a))
|
|
a.UdpNetwork = "udp6"
|
|
ret = append(ret, announce(a))
|
|
return
|
|
}
|
|
return []announceResult{announce(a)}
|
|
}
|
|
|
|
func announce(a tracker.Announce) announceResult {
|
|
resp, err := a.Do()
|
|
return announceResult{resp, err}
|
|
}
|