package torrent
 
 import (
+       "context"
        "net"
        "net/http"
        "net/url"
        // Defines proxy for HTTP requests, such as for trackers. It's commonly set from the result of
        // "net/http".ProxyURL(HTTPProxy).
        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.
        // Used in case DNS lookups require a special setup (i.e., dns-over-https)
        LookupTrackerIp func(*url.URL) ([]net.IP, error)
 
 
 import (
        "context"
+       "net"
        "net/url"
 
        "github.com/anacrolix/log"
 type NewClientOpts struct {
        Http trHttp.NewClientOpts
        // Overrides the network in the scheme. Probably a legacy thing.
-       UdpNetwork string
-       Logger     log.Logger
+       UdpNetwork   string
+       Logger       log.Logger
+       ListenPacket func(network, addr string) (net.PacketConn, error)
 }
 
 func NewClient(urlStr string, opts NewClientOpts) (Client, error) {
                        network = opts.UdpNetwork
                }
                cc, err := udp.NewConnClient(udp.NewConnClientOpts{
-                       Network: network,
-                       Host:    _url.Host,
-                       Logger:  opts.Logger,
+                       Network:      network,
+                       Host:         _url.Host,
+                       Logger:       opts.Logger,
+                       ListenPacket: opts.ListenPacket,
                })
                if err != nil {
                        return nil, err
 
 package http
 
 import (
+       "context"
        "crypto/tls"
+       "net"
        "net/http"
        "net/url"
 )
 }
 
 type ProxyFunc func(*http.Request) (*url.URL, error)
+type DialContextFunc func(ctx context.Context, network, addr string) (net.Conn, error)
 
 type NewClientOpts struct {
        Proxy          ProxyFunc
+       DialContext    DialContextFunc
        ServerName     string
        AllowKeepAlive bool
 }
                url_: url_,
                hc: &http.Client{
                        Transport: &http.Transport{
+                               DialContext: opts.DialContext,
                                Proxy: opts.Proxy,
                                TLSClientConfig: &tls.Config{
                                        InsecureSkipVerify: true,
 
        "context"
        "errors"
        "fmt"
+       "net"
        "net/http"
        "net/url"
        "time"
 var ErrBadScheme = errors.New("unknown scheme")
 
 type Announce struct {
-       TrackerUrl string
-       Request    AnnounceRequest
-       HostHeader string
-       HTTPProxy  func(*http.Request) (*url.URL, error)
-       ServerName string
-       UserAgent  string
-       UdpNetwork string
+       TrackerUrl   string
+       Request      AnnounceRequest
+       HostHeader   string
+       HTTPProxy    func(*http.Request) (*url.URL, error)
+       DialContext  func(ctx context.Context, network, addr string) (net.Conn, error)
+       ListenPacket func(network, addr string) (net.PacketConn, error)
+       ServerName   string
+       UserAgent    string
+       UdpNetwork   string
        // If the port is zero, it's assumed to be the same as the Request.Port.
        ClientIp4 krpc.NodeAddr
        // If the port is zero, it's assumed to be the same as the Request.Port.
 func (me Announce) Do() (res AnnounceResponse, err error) {
        cl, err := NewClient(me.TrackerUrl, NewClientOpts{
                Http: trHttp.NewClientOpts{
-                       Proxy:      me.HTTPProxy,
-                       ServerName: me.ServerName,
+                       Proxy:       me.HTTPProxy,
+                       DialContext: me.DialContext,
+                       ServerName:  me.ServerName,
                },
-               UdpNetwork: me.UdpNetwork,
-               Logger:     me.Logger.WithContextValue(fmt.Sprintf("tracker client for %q", me.TrackerUrl)),
+               UdpNetwork:   me.UdpNetwork,
+               Logger:       me.Logger.WithContextValue(fmt.Sprintf("tracker client for %q", me.TrackerUrl)),
+               ListenPacket: me.ListenPacket,
        })
        if err != nil {
                return
 
        "github.com/anacrolix/missinggo/v2"
 )
 
+type listenPacketFunc func(network, addr string) (net.PacketConn, error)
+
 type NewConnClientOpts struct {
        // The network to operate to use, such as "udp4", "udp", "udp6".
        Network string
        Ipv6 *bool
        // Logger to use for internal errors.
        Logger log.Logger
+       // Custom function to use as a substitute for net.ListenPacket
+       ListenPacket listenPacketFunc
 }
 
 // Manages a Client with a specific connection.
 }
 
 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 {
                return
        }
 
        defer cancel()
        me.t.logger.WithDefaultLevel(log.Debug).Printf("announcing to %q: %#v", me.u.String(), req)
        res, err := tracker.Announce{
-               Context:    ctx,
-               HTTPProxy:  me.t.cl.config.HTTPProxy,
-               UserAgent:  me.t.cl.config.HTTPUserAgent,
-               TrackerUrl: me.trackerUrl(ip),
-               Request:    req,
-               HostHeader: me.u.Host,
-               ServerName: me.u.Hostname(),
-               UdpNetwork: me.u.Scheme,
-               ClientIp4:  krpc.NodeAddr{IP: me.t.cl.config.PublicIp4},
-               ClientIp6:  krpc.NodeAddr{IP: me.t.cl.config.PublicIp6},
-               Logger:     me.t.logger,
+               Context:      ctx,
+               HTTPProxy:    me.t.cl.config.HTTPProxy,
+               DialContext:  me.t.cl.config.TrackerDialContext,
+               ListenPacket: me.t.cl.config.TrackerListenPacket,
+               UserAgent:    me.t.cl.config.HTTPUserAgent,
+               TrackerUrl:   me.trackerUrl(ip),
+               Request:      req,
+               HostHeader:   me.u.Host,
+               ServerName:   me.u.Hostname(),
+               UdpNetwork:   me.u.Scheme,
+               ClientIp4:    krpc.NodeAddr{IP: me.t.cl.config.PublicIp4},
+               ClientIp6:    krpc.NodeAddr{IP: me.t.cl.config.PublicIp6},
+               Logger:       me.t.logger,
        }.Do()
        me.t.logger.WithDefaultLevel(log.Debug).Printf("announce to %q returned %#v: %v", me.u.String(), res, err)
        if err != nil {