init
This commit is contained in:
7
vendor/github.com/anacrolix/torrent/tracker/expvar.go
generated
vendored
Normal file
7
vendor/github.com/anacrolix/torrent/tracker/expvar.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"expvar"
|
||||
)
|
||||
|
||||
var vars = expvar.NewMap("tracker")
|
140
vendor/github.com/anacrolix/torrent/tracker/http.go
generated
vendored
Normal file
140
vendor/github.com/anacrolix/torrent/tracker/http.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/anacrolix/dht/krpc"
|
||||
"github.com/anacrolix/missinggo/httptoo"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
)
|
||||
|
||||
type httpResponse struct {
|
||||
FailureReason string `bencode:"failure reason"`
|
||||
Interval int32 `bencode:"interval"`
|
||||
TrackerId string `bencode:"tracker id"`
|
||||
Complete int32 `bencode:"complete"`
|
||||
Incomplete int32 `bencode:"incomplete"`
|
||||
Peers Peers `bencode:"peers"`
|
||||
// BEP 7
|
||||
Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
|
||||
}
|
||||
|
||||
type Peers []Peer
|
||||
|
||||
func (me *Peers) UnmarshalBencode(b []byte) (err error) {
|
||||
var _v interface{}
|
||||
err = bencode.Unmarshal(b, &_v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch v := _v.(type) {
|
||||
case string:
|
||||
vars.Add("http responses with string peers", 1)
|
||||
var cnas krpc.CompactIPv4NodeAddrs
|
||||
err = cnas.UnmarshalBinary([]byte(v))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, cp := range cnas {
|
||||
*me = append(*me, Peer{
|
||||
IP: cp.IP[:],
|
||||
Port: int(cp.Port),
|
||||
})
|
||||
}
|
||||
return
|
||||
case []interface{}:
|
||||
vars.Add("http responses with list peers", 1)
|
||||
for _, i := range v {
|
||||
var p Peer
|
||||
p.fromDictInterface(i.(map[string]interface{}))
|
||||
*me = append(*me, p)
|
||||
}
|
||||
return
|
||||
default:
|
||||
vars.Add("http responses with unhandled peers type", 1)
|
||||
err = fmt.Errorf("unsupported type: %T", _v)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) {
|
||||
q := _url.Query()
|
||||
|
||||
q.Set("info_hash", string(ar.InfoHash[:]))
|
||||
q.Set("peer_id", string(ar.PeerId[:]))
|
||||
// AFAICT, port is mandatory, and there's no implied port key.
|
||||
q.Set("port", fmt.Sprintf("%d", ar.Port))
|
||||
q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10))
|
||||
q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10))
|
||||
q.Set("left", strconv.FormatUint(ar.Left, 10))
|
||||
if ar.Event != None {
|
||||
q.Set("event", ar.Event.String())
|
||||
}
|
||||
// http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
|
||||
q.Set("compact", "1")
|
||||
// According to https://wiki.vuze.com/w/Message_Stream_Encryption. TODO:
|
||||
// Take EncryptionPolicy or something like it as a parameter.
|
||||
q.Set("supportcrypto", "1")
|
||||
if opts.ClientIp4.IP != nil {
|
||||
q.Set("ipv4", opts.ClientIp4.String())
|
||||
}
|
||||
if opts.ClientIp6.IP != nil {
|
||||
q.Set("ipv6", opts.ClientIp6.String())
|
||||
}
|
||||
_url.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
func announceHTTP(opt Announce, _url *url.URL) (ret AnnounceResponse, err error) {
|
||||
_url = httptoo.CopyURL(_url)
|
||||
setAnnounceParams(_url, &opt.Request, opt)
|
||||
req, err := http.NewRequest("GET", _url.String(), nil)
|
||||
req.Header.Set("User-Agent", opt.UserAgent)
|
||||
req.Host = opt.HostHeader
|
||||
resp, err := opt.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, resp.Body)
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
|
||||
return
|
||||
}
|
||||
var trackerResponse httpResponse
|
||||
err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
|
||||
if _, ok := err.(bencode.ErrUnusedTrailingBytes); ok {
|
||||
err = nil
|
||||
} else if err != nil {
|
||||
err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
|
||||
return
|
||||
}
|
||||
if trackerResponse.FailureReason != "" {
|
||||
err = fmt.Errorf("tracker gave failure reason: %q", trackerResponse.FailureReason)
|
||||
return
|
||||
}
|
||||
vars.Add("successful http announces", 1)
|
||||
ret.Interval = trackerResponse.Interval
|
||||
ret.Leechers = trackerResponse.Incomplete
|
||||
ret.Seeders = trackerResponse.Complete
|
||||
if len(trackerResponse.Peers) != 0 {
|
||||
vars.Add("http responses with nonempty peers key", 1)
|
||||
}
|
||||
ret.Peers = trackerResponse.Peers
|
||||
if len(trackerResponse.Peers6) != 0 {
|
||||
vars.Add("http responses with nonempty peers6 key", 1)
|
||||
}
|
||||
for _, na := range trackerResponse.Peers6 {
|
||||
ret.Peers = append(ret.Peers, Peer{
|
||||
IP: na.IP,
|
||||
Port: na.Port,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
26
vendor/github.com/anacrolix/torrent/tracker/peer.go
generated
vendored
Normal file
26
vendor/github.com/anacrolix/torrent/tracker/peer.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/anacrolix/dht/krpc"
|
||||
)
|
||||
|
||||
type Peer struct {
|
||||
IP net.IP
|
||||
Port int
|
||||
ID []byte
|
||||
}
|
||||
|
||||
// Set from the non-compact form in BEP 3.
|
||||
func (p *Peer) fromDictInterface(d map[string]interface{}) {
|
||||
p.IP = net.ParseIP(d["ip"].(string))
|
||||
p.ID = []byte(d["peer id"].(string))
|
||||
p.Port = int(d["port"].(int64))
|
||||
}
|
||||
|
||||
func (p Peer) FromNodeAddr(na krpc.NodeAddr) Peer {
|
||||
p.IP = na.IP
|
||||
p.Port = na.Port
|
||||
return p
|
||||
}
|
124
vendor/github.com/anacrolix/torrent/tracker/server.go
generated
vendored
Normal file
124
vendor/github.com/anacrolix/torrent/tracker/server.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
|
||||
"github.com/anacrolix/dht/krpc"
|
||||
"github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
type torrent struct {
|
||||
Leechers int32
|
||||
Seeders int32
|
||||
Peers []krpc.NodeAddr
|
||||
}
|
||||
|
||||
type server struct {
|
||||
pc net.PacketConn
|
||||
conns map[int64]struct{}
|
||||
t map[[20]byte]torrent
|
||||
}
|
||||
|
||||
func marshal(parts ...interface{}) (ret []byte, err error) {
|
||||
var buf bytes.Buffer
|
||||
for _, p := range parts {
|
||||
err = binary.Write(&buf, binary.BigEndian, p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
ret = buf.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *server) respond(addr net.Addr, rh ResponseHeader, parts ...interface{}) (err error) {
|
||||
b, err := marshal(append([]interface{}{rh}, parts...)...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = s.pc.WriteTo(b, addr)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *server) newConn() (ret int64) {
|
||||
ret = rand.Int63()
|
||||
if s.conns == nil {
|
||||
s.conns = make(map[int64]struct{})
|
||||
}
|
||||
s.conns[ret] = struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *server) serveOne() (err error) {
|
||||
b := make([]byte, 0x10000)
|
||||
n, addr, err := s.pc.ReadFrom(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r := bytes.NewReader(b[:n])
|
||||
var h RequestHeader
|
||||
err = readBody(r, &h)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch h.Action {
|
||||
case ActionConnect:
|
||||
if h.ConnectionId != connectRequestConnectionId {
|
||||
return
|
||||
}
|
||||
connId := s.newConn()
|
||||
err = s.respond(addr, ResponseHeader{
|
||||
ActionConnect,
|
||||
h.TransactionId,
|
||||
}, ConnectionResponse{
|
||||
connId,
|
||||
})
|
||||
return
|
||||
case ActionAnnounce:
|
||||
if _, ok := s.conns[h.ConnectionId]; !ok {
|
||||
s.respond(addr, ResponseHeader{
|
||||
TransactionId: h.TransactionId,
|
||||
Action: ActionError,
|
||||
}, []byte("not connected"))
|
||||
return
|
||||
}
|
||||
var ar AnnounceRequest
|
||||
err = readBody(r, &ar)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t := s.t[ar.InfoHash]
|
||||
bm := func() encoding.BinaryMarshaler {
|
||||
ip := missinggo.AddrIP(addr)
|
||||
if ip.To4() != nil {
|
||||
return krpc.CompactIPv4NodeAddrs(t.Peers)
|
||||
}
|
||||
return krpc.CompactIPv6NodeAddrs(t.Peers)
|
||||
}()
|
||||
b, err = bm.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = s.respond(addr, ResponseHeader{
|
||||
TransactionId: h.TransactionId,
|
||||
Action: ActionAnnounce,
|
||||
}, AnnounceResponseHeader{
|
||||
Interval: 900,
|
||||
Leechers: t.Leechers,
|
||||
Seeders: t.Seeders,
|
||||
}, b)
|
||||
return
|
||||
default:
|
||||
err = fmt.Errorf("unhandled action: %d", h.Action)
|
||||
s.respond(addr, ResponseHeader{
|
||||
TransactionId: h.TransactionId,
|
||||
Action: ActionError,
|
||||
}, []byte("unhandled action"))
|
||||
return
|
||||
}
|
||||
}
|
81
vendor/github.com/anacrolix/torrent/tracker/tracker.go
generated
vendored
Normal file
81
vendor/github.com/anacrolix/torrent/tracker/tracker.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/anacrolix/dht/krpc"
|
||||
)
|
||||
|
||||
// Marshalled as binary by the UDP client, so be careful making changes.
|
||||
type AnnounceRequest struct {
|
||||
InfoHash [20]byte
|
||||
PeerId [20]byte
|
||||
Downloaded int64
|
||||
Left uint64
|
||||
Uploaded int64
|
||||
// Apparently this is optional. None can be used for announces done at
|
||||
// regular intervals.
|
||||
Event AnnounceEvent
|
||||
IPAddress uint32
|
||||
Key int32
|
||||
NumWant int32 // How many peer addresses are desired. -1 for default.
|
||||
Port uint16
|
||||
} // 82 bytes
|
||||
|
||||
type AnnounceResponse struct {
|
||||
Interval int32 // Minimum seconds the local peer should wait before next announce.
|
||||
Leechers int32
|
||||
Seeders int32
|
||||
Peers []Peer
|
||||
}
|
||||
|
||||
type AnnounceEvent int32
|
||||
|
||||
func (e AnnounceEvent) String() string {
|
||||
// See BEP 3, "event".
|
||||
return []string{"empty", "completed", "started", "stopped"}[e]
|
||||
}
|
||||
|
||||
const (
|
||||
None AnnounceEvent = iota
|
||||
Completed // The local peer just completed the torrent.
|
||||
Started // The local peer has just resumed this torrent.
|
||||
Stopped // The local peer is leaving the swarm.
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadScheme = errors.New("unknown scheme")
|
||||
)
|
||||
|
||||
type Announce struct {
|
||||
TrackerUrl string
|
||||
Request AnnounceRequest
|
||||
HostHeader string
|
||||
UserAgent string
|
||||
HttpClient *http.Client
|
||||
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
|
||||
ClientIp6 krpc.NodeAddr
|
||||
}
|
||||
|
||||
// In an FP language with currying, what order what you put these params?
|
||||
|
||||
func (me Announce) Do() (res AnnounceResponse, err error) {
|
||||
_url, err := url.Parse(me.TrackerUrl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch _url.Scheme {
|
||||
case "http", "https":
|
||||
return announceHTTP(me, _url)
|
||||
case "udp", "udp4", "udp6":
|
||||
return announceUDP(me, _url)
|
||||
default:
|
||||
err = ErrBadScheme
|
||||
return
|
||||
}
|
||||
}
|
298
vendor/github.com/anacrolix/torrent/tracker/udp.go
generated
vendored
Normal file
298
vendor/github.com/anacrolix/torrent/tracker/udp.go
generated
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/dht/krpc"
|
||||
"github.com/anacrolix/missinggo"
|
||||
"github.com/anacrolix/missinggo/pproffd"
|
||||
)
|
||||
|
||||
type Action int32
|
||||
|
||||
const (
|
||||
ActionConnect Action = iota
|
||||
ActionAnnounce
|
||||
ActionScrape
|
||||
ActionError
|
||||
|
||||
connectRequestConnectionId = 0x41727101980
|
||||
|
||||
// BEP 41
|
||||
optionTypeEndOfOptions = 0
|
||||
optionTypeNOP = 1
|
||||
optionTypeURLData = 2
|
||||
)
|
||||
|
||||
type ConnectionRequest struct {
|
||||
ConnectionId int64
|
||||
Action int32
|
||||
TransctionId int32
|
||||
}
|
||||
|
||||
type ConnectionResponse struct {
|
||||
ConnectionId int64
|
||||
}
|
||||
|
||||
type ResponseHeader struct {
|
||||
Action Action
|
||||
TransactionId int32
|
||||
}
|
||||
|
||||
type RequestHeader struct {
|
||||
ConnectionId int64
|
||||
Action Action
|
||||
TransactionId int32
|
||||
} // 16 bytes
|
||||
|
||||
type AnnounceResponseHeader struct {
|
||||
Interval int32
|
||||
Leechers int32
|
||||
Seeders int32
|
||||
}
|
||||
|
||||
func newTransactionId() int32 {
|
||||
return int32(rand.Uint32())
|
||||
}
|
||||
|
||||
func timeout(contiguousTimeouts int) (d time.Duration) {
|
||||
if contiguousTimeouts > 8 {
|
||||
contiguousTimeouts = 8
|
||||
}
|
||||
d = 15 * time.Second
|
||||
for ; contiguousTimeouts > 0; contiguousTimeouts-- {
|
||||
d *= 2
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type udpAnnounce struct {
|
||||
contiguousTimeouts int
|
||||
connectionIdReceived time.Time
|
||||
connectionId int64
|
||||
socket net.Conn
|
||||
url url.URL
|
||||
a *Announce
|
||||
}
|
||||
|
||||
func (c *udpAnnounce) Close() error {
|
||||
if c.socket != nil {
|
||||
return c.socket.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpAnnounce) ipv6() bool {
|
||||
if c.a.UdpNetwork == "udp6" {
|
||||
return true
|
||||
}
|
||||
rip := missinggo.AddrIP(c.socket.RemoteAddr())
|
||||
return rip.To16() != nil && rip.To4() == nil
|
||||
}
|
||||
|
||||
func (c *udpAnnounce) Do(req AnnounceRequest) (res AnnounceResponse, err error) {
|
||||
err = c.connect()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
reqURI := c.url.RequestURI()
|
||||
if c.ipv6() {
|
||||
// BEP 15
|
||||
req.IPAddress = 0
|
||||
} else if req.IPAddress == 0 && c.a.ClientIp4.IP != nil {
|
||||
req.IPAddress = binary.BigEndian.Uint32(c.a.ClientIp4.IP.To4())
|
||||
}
|
||||
// Clearly this limits the request URI to 255 bytes. BEP 41 supports
|
||||
// longer but I'm not fussed.
|
||||
options := append([]byte{optionTypeURLData, byte(len(reqURI))}, []byte(reqURI)...)
|
||||
b, err := c.request(ActionAnnounce, req, options)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var h AnnounceResponseHeader
|
||||
err = readBody(b, &h)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
err = fmt.Errorf("error parsing announce response: %s", err)
|
||||
return
|
||||
}
|
||||
res.Interval = h.Interval
|
||||
res.Leechers = h.Leechers
|
||||
res.Seeders = h.Seeders
|
||||
nas := func() interface {
|
||||
encoding.BinaryUnmarshaler
|
||||
NodeAddrs() []krpc.NodeAddr
|
||||
} {
|
||||
if c.ipv6() {
|
||||
return &krpc.CompactIPv6NodeAddrs{}
|
||||
} else {
|
||||
return &krpc.CompactIPv4NodeAddrs{}
|
||||
}
|
||||
}()
|
||||
err = nas.UnmarshalBinary(b.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, cp := range nas.NodeAddrs() {
|
||||
res.Peers = append(res.Peers, Peer{}.FromNodeAddr(cp))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// body is the binary serializable request body. trailer is optional data
|
||||
// following it, such as for BEP 41.
|
||||
func (c *udpAnnounce) write(h *RequestHeader, body interface{}, trailer []byte) (err error) {
|
||||
var buf bytes.Buffer
|
||||
err = binary.Write(&buf, binary.BigEndian, h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if body != nil {
|
||||
err = binary.Write(&buf, binary.BigEndian, body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
_, err = buf.Write(trailer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n, err := c.socket.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n != buf.Len() {
|
||||
panic("write should send all or error")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func read(r io.Reader, data interface{}) error {
|
||||
return binary.Read(r, binary.BigEndian, data)
|
||||
}
|
||||
|
||||
func write(w io.Writer, data interface{}) error {
|
||||
return binary.Write(w, binary.BigEndian, data)
|
||||
}
|
||||
|
||||
// args is the binary serializable request body. trailer is optional data
|
||||
// following it, such as for BEP 41.
|
||||
func (c *udpAnnounce) request(action Action, args interface{}, options []byte) (responseBody *bytes.Buffer, err error) {
|
||||
tid := newTransactionId()
|
||||
err = c.write(&RequestHeader{
|
||||
ConnectionId: c.connectionId,
|
||||
Action: action,
|
||||
TransactionId: tid,
|
||||
}, args, options)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.socket.SetReadDeadline(time.Now().Add(timeout(c.contiguousTimeouts)))
|
||||
b := make([]byte, 0x800) // 2KiB
|
||||
for {
|
||||
var n int
|
||||
n, err = c.socket.Read(b)
|
||||
if opE, ok := err.(*net.OpError); ok {
|
||||
if opE.Timeout() {
|
||||
c.contiguousTimeouts++
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := bytes.NewBuffer(b[:n])
|
||||
var h ResponseHeader
|
||||
err = binary.Read(buf, binary.BigEndian, &h)
|
||||
switch err {
|
||||
case io.ErrUnexpectedEOF:
|
||||
continue
|
||||
case nil:
|
||||
default:
|
||||
return
|
||||
}
|
||||
if h.TransactionId != tid {
|
||||
continue
|
||||
}
|
||||
c.contiguousTimeouts = 0
|
||||
if h.Action == ActionError {
|
||||
err = errors.New(buf.String())
|
||||
}
|
||||
responseBody = buf
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func readBody(r io.Reader, data ...interface{}) (err error) {
|
||||
for _, datum := range data {
|
||||
err = binary.Read(r, binary.BigEndian, datum)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpAnnounce) connected() bool {
|
||||
return !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute))
|
||||
}
|
||||
|
||||
func (c *udpAnnounce) dialNetwork() string {
|
||||
if c.a.UdpNetwork != "" {
|
||||
return c.a.UdpNetwork
|
||||
}
|
||||
return "udp"
|
||||
}
|
||||
|
||||
func (c *udpAnnounce) connect() (err error) {
|
||||
if c.connected() {
|
||||
return nil
|
||||
}
|
||||
c.connectionId = connectRequestConnectionId
|
||||
if c.socket == nil {
|
||||
hmp := missinggo.SplitHostMaybePort(c.url.Host)
|
||||
if hmp.NoPort {
|
||||
hmp.NoPort = false
|
||||
hmp.Port = 80
|
||||
}
|
||||
c.socket, err = net.Dial(c.dialNetwork(), hmp.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.socket = pproffd.WrapNetConn(c.socket)
|
||||
}
|
||||
b, err := c.request(ActionConnect, nil, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var res ConnectionResponse
|
||||
err = readBody(b, &res)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.connectionId = res.ConnectionId
|
||||
c.connectionIdReceived = time.Now()
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Split on IPv6, as BEP 15 says response peer decoding depends on
|
||||
// network in use.
|
||||
func announceUDP(opt Announce, _url *url.URL) (AnnounceResponse, error) {
|
||||
ua := udpAnnounce{
|
||||
url: *_url,
|
||||
a: &opt,
|
||||
}
|
||||
defer ua.Close()
|
||||
return ua.Do(opt.Request)
|
||||
}
|
Reference in New Issue
Block a user