2
0
mirror of synced 2025-02-23 06:08:07 +00:00

Add ability to set DialContext/ListenPacket for tracker announcements (#760)

This is useful if you want to use a custom dialer to proxy requests via
an external server since the HTTPProxy can only be used with tcp
trackers and not udp.
This commit is contained in:
Craig Campbell 2022-07-07 01:51:58 -04:00 committed by GitHub
parent 75cc4e98d4
commit f120b93e1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 28 deletions

View File

@ -1,6 +1,7 @@
package torrent package torrent
import ( import (
"context"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -90,6 +91,10 @@ type ClientConfig struct {
// Defines proxy for HTTP requests, such as for trackers. It's commonly set from the result of // Defines proxy for HTTP requests, such as for trackers. It's commonly set from the result of
// "net/http".ProxyURL(HTTPProxy). // "net/http".ProxyURL(HTTPProxy).
HTTPProxy func(*http.Request) (*url.URL, error) HTTPProxy func(*http.Request) (*url.URL, error)
// Defines DialContext func to use for HTTP tracker announcements
TrackerDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
// Defines ListenPacket func to use for UDP tracker announcements
TrackerListenPacket func(network, addr string) (net.PacketConn, error)
// Takes a tracker's hostname and requests DNS A and AAAA records. // Takes a tracker's hostname and requests DNS A and AAAA records.
// Used in case DNS lookups require a special setup (i.e., dns-over-https) // Used in case DNS lookups require a special setup (i.e., dns-over-https)
LookupTrackerIp func(*url.URL) ([]net.IP, error) LookupTrackerIp func(*url.URL) ([]net.IP, error)

View File

@ -2,6 +2,7 @@ package tracker
import ( import (
"context" "context"
"net"
"net/url" "net/url"
"github.com/anacrolix/log" "github.com/anacrolix/log"
@ -19,8 +20,9 @@ type AnnounceOpt = trHttp.AnnounceOpt
type NewClientOpts struct { type NewClientOpts struct {
Http trHttp.NewClientOpts Http trHttp.NewClientOpts
// Overrides the network in the scheme. Probably a legacy thing. // Overrides the network in the scheme. Probably a legacy thing.
UdpNetwork string UdpNetwork string
Logger log.Logger Logger log.Logger
ListenPacket func(network, addr string) (net.PacketConn, error)
} }
func NewClient(urlStr string, opts NewClientOpts) (Client, error) { func NewClient(urlStr string, opts NewClientOpts) (Client, error) {
@ -37,9 +39,10 @@ func NewClient(urlStr string, opts NewClientOpts) (Client, error) {
network = opts.UdpNetwork network = opts.UdpNetwork
} }
cc, err := udp.NewConnClient(udp.NewConnClientOpts{ cc, err := udp.NewConnClient(udp.NewConnClientOpts{
Network: network, Network: network,
Host: _url.Host, Host: _url.Host,
Logger: opts.Logger, Logger: opts.Logger,
ListenPacket: opts.ListenPacket,
}) })
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -1,7 +1,9 @@
package http package http
import ( import (
"context"
"crypto/tls" "crypto/tls"
"net"
"net/http" "net/http"
"net/url" "net/url"
) )
@ -12,9 +14,11 @@ type Client struct {
} }
type ProxyFunc func(*http.Request) (*url.URL, error) type ProxyFunc func(*http.Request) (*url.URL, error)
type DialContextFunc func(ctx context.Context, network, addr string) (net.Conn, error)
type NewClientOpts struct { type NewClientOpts struct {
Proxy ProxyFunc Proxy ProxyFunc
DialContext DialContextFunc
ServerName string ServerName string
AllowKeepAlive bool AllowKeepAlive bool
} }
@ -24,6 +28,7 @@ func NewClient(url_ *url.URL, opts NewClientOpts) Client {
url_: url_, url_: url_,
hc: &http.Client{ hc: &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
DialContext: opts.DialContext,
Proxy: opts.Proxy, Proxy: opts.Proxy,
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net"
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
@ -33,13 +34,15 @@ type AnnounceEvent = udp.AnnounceEvent
var ErrBadScheme = errors.New("unknown scheme") var ErrBadScheme = errors.New("unknown scheme")
type Announce struct { type Announce struct {
TrackerUrl string TrackerUrl string
Request AnnounceRequest Request AnnounceRequest
HostHeader string HostHeader string
HTTPProxy func(*http.Request) (*url.URL, error) HTTPProxy func(*http.Request) (*url.URL, error)
ServerName string DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
UserAgent string ListenPacket func(network, addr string) (net.PacketConn, error)
UdpNetwork string ServerName string
UserAgent string
UdpNetwork string
// If the port is zero, it's assumed to be the same as the Request.Port. // If the port is zero, it's assumed to be the same as the Request.Port.
ClientIp4 krpc.NodeAddr ClientIp4 krpc.NodeAddr
// If the port is zero, it's assumed to be the same as the Request.Port. // If the port is zero, it's assumed to be the same as the Request.Port.
@ -54,11 +57,13 @@ const DefaultTrackerAnnounceTimeout = 15 * time.Second
func (me Announce) Do() (res AnnounceResponse, err error) { func (me Announce) Do() (res AnnounceResponse, err error) {
cl, err := NewClient(me.TrackerUrl, NewClientOpts{ cl, err := NewClient(me.TrackerUrl, NewClientOpts{
Http: trHttp.NewClientOpts{ Http: trHttp.NewClientOpts{
Proxy: me.HTTPProxy, Proxy: me.HTTPProxy,
ServerName: me.ServerName, DialContext: me.DialContext,
ServerName: me.ServerName,
}, },
UdpNetwork: me.UdpNetwork, UdpNetwork: me.UdpNetwork,
Logger: me.Logger.WithContextValue(fmt.Sprintf("tracker client for %q", me.TrackerUrl)), Logger: me.Logger.WithContextValue(fmt.Sprintf("tracker client for %q", me.TrackerUrl)),
ListenPacket: me.ListenPacket,
}) })
if err != nil { if err != nil {
return return

View File

@ -9,6 +9,8 @@ import (
"github.com/anacrolix/missinggo/v2" "github.com/anacrolix/missinggo/v2"
) )
type listenPacketFunc func(network, addr string) (net.PacketConn, error)
type NewConnClientOpts struct { type NewConnClientOpts struct {
// The network to operate to use, such as "udp4", "udp", "udp6". // The network to operate to use, such as "udp4", "udp", "udp6".
Network string Network string
@ -18,6 +20,8 @@ type NewConnClientOpts struct {
Ipv6 *bool Ipv6 *bool
// Logger to use for internal errors. // Logger to use for internal errors.
Logger log.Logger Logger log.Logger
// Custom function to use as a substitute for net.ListenPacket
ListenPacket listenPacketFunc
} }
// Manages a Client with a specific connection. // Manages a Client with a specific connection.
@ -80,7 +84,13 @@ func (me clientWriter) Write(p []byte) (n int, err error) {
} }
func NewConnClient(opts NewConnClientOpts) (cc *ConnClient, err error) { func NewConnClient(opts NewConnClientOpts) (cc *ConnClient, err error) {
conn, err := net.ListenPacket(opts.Network, ":0") var conn net.PacketConn
if opts.ListenPacket != nil {
conn, err = opts.ListenPacket(opts.Network, ":0")
} else {
conn, err = net.ListenPacket(opts.Network, ":0")
}
if err != nil { if err != nil {
return return
} }

View File

@ -156,17 +156,19 @@ func (me *trackerScraper) announce(ctx context.Context, event tracker.AnnounceEv
defer cancel() defer cancel()
me.t.logger.WithDefaultLevel(log.Debug).Printf("announcing to %q: %#v", me.u.String(), req) me.t.logger.WithDefaultLevel(log.Debug).Printf("announcing to %q: %#v", me.u.String(), req)
res, err := tracker.Announce{ res, err := tracker.Announce{
Context: ctx, Context: ctx,
HTTPProxy: me.t.cl.config.HTTPProxy, HTTPProxy: me.t.cl.config.HTTPProxy,
UserAgent: me.t.cl.config.HTTPUserAgent, DialContext: me.t.cl.config.TrackerDialContext,
TrackerUrl: me.trackerUrl(ip), ListenPacket: me.t.cl.config.TrackerListenPacket,
Request: req, UserAgent: me.t.cl.config.HTTPUserAgent,
HostHeader: me.u.Host, TrackerUrl: me.trackerUrl(ip),
ServerName: me.u.Hostname(), Request: req,
UdpNetwork: me.u.Scheme, HostHeader: me.u.Host,
ClientIp4: krpc.NodeAddr{IP: me.t.cl.config.PublicIp4}, ServerName: me.u.Hostname(),
ClientIp6: krpc.NodeAddr{IP: me.t.cl.config.PublicIp6}, UdpNetwork: me.u.Scheme,
Logger: me.t.logger, ClientIp4: krpc.NodeAddr{IP: me.t.cl.config.PublicIp4},
ClientIp6: krpc.NodeAddr{IP: me.t.cl.config.PublicIp6},
Logger: me.t.logger,
}.Do() }.Do()
me.t.logger.WithDefaultLevel(log.Debug).Printf("announce to %q returned %#v: %v", me.u.String(), res, err) me.t.logger.WithDefaultLevel(log.Debug).Printf("announce to %q returned %#v: %v", me.u.String(), res, err)
if err != nil { if err != nil {