diff --git a/client.go b/client.go index 2e8c5575..bda89c6f 100644 --- a/client.go +++ b/client.go @@ -677,7 +677,7 @@ func (cl *Client) initiateProtocolHandshakes( nc net.Conn, t *Torrent, outgoing, encryptHeader bool, - remoteAddr net.Addr, + remoteAddr PeerRemoteAddr, network, connString string, ) ( c *PeerConn, err error, @@ -699,7 +699,7 @@ func (cl *Client) initiateProtocolHandshakes( } // Returns nil connection and nil error if no connection could be established for valid reasons. -func (cl *Client) establishOutgoingConnEx(t *Torrent, addr net.Addr, obfuscatedHeader bool) (*PeerConn, error) { +func (cl *Client) establishOutgoingConnEx(t *Torrent, addr PeerRemoteAddr, obfuscatedHeader bool) (*PeerConn, error) { dialCtx, cancel := context.WithTimeout(context.Background(), func() time.Duration { cl.rLock() defer cl.rUnlock() @@ -723,7 +723,7 @@ func (cl *Client) establishOutgoingConnEx(t *Torrent, addr net.Addr, obfuscatedH // Returns nil connection and nil error if no connection could be established // for valid reasons. -func (cl *Client) establishOutgoingConn(t *Torrent, addr net.Addr) (c *PeerConn, err error) { +func (cl *Client) establishOutgoingConn(t *Torrent, addr PeerRemoteAddr) (c *PeerConn, err error) { torrent.Add("establish outgoing connection", 1) obfuscatedHeaderFirst := cl.config.HeaderObfuscationPolicy.Preferred c, err = cl.establishOutgoingConnEx(t, addr, obfuscatedHeaderFirst) @@ -748,7 +748,7 @@ func (cl *Client) establishOutgoingConn(t *Torrent, addr net.Addr) (c *PeerConn, // Called to dial out and run a connection. The addr we're given is already // considered half-open. -func (cl *Client) outgoingConnection(t *Torrent, addr net.Addr, ps PeerSource, trusted bool) { +func (cl *Client) outgoingConnection(t *Torrent, addr PeerRemoteAddr, ps PeerSource, trusted bool) { cl.dialRateLimiter.Wait(context.Background()) c, err := cl.establishOutgoingConn(t, addr) cl.lock() @@ -967,7 +967,7 @@ func (cl *Client) sendInitialMessages(conn *PeerConn, torrent *Torrent) { // that might be used to cache pending writes. Assuming 512KiB cached for // sending, for 16KiB chunks. Reqq: 1 << 5, - YourIp: pp.CompactIp(addrIpOrNil(conn.RemoteAddr)), + YourIp: pp.CompactIp(conn.remoteIp()), Encryption: cl.config.HeaderObfuscationPolicy.Preferred || !cl.config.HeaderObfuscationPolicy.RequirePreferred, Port: cl.incomingPeerPort(), MetadataSize: torrent.metadataSize(), @@ -1062,7 +1062,7 @@ func (cl *Client) gotMetadataExtensionMsg(payload []byte, t *Torrent, c *PeerCon } } -func (cl *Client) badPeerAddr(addr net.Addr) bool { +func (cl *Client) badPeerAddr(addr PeerRemoteAddr) bool { if ipa, ok := tryIpPortFromNetAddr(addr); ok { return cl.badPeerIPPort(ipa.IP, ipa.Port) } @@ -1099,7 +1099,8 @@ func (cl *Client) newTorrent(ih metainfo.Hash, specStorage storage.ClientImpl) ( peers: prioritizedPeers{ om: btree.New(32), getPrio: func(p PeerInfo) peerPriority { - return bep40PriorityIgnoreError(cl.publicAddr(addrIpOrNil(p.Addr)), p.addr()) + ipPort := p.addr() + return bep40PriorityIgnoreError(cl.publicAddr(ipPort.IP), ipPort) }, }, conns: make(map[*PeerConn]struct{}, 2*cl.config.EstablishedConnsPerTorrent), @@ -1366,7 +1367,7 @@ func (cl *Client) banPeerIP(ip net.IP) { cl.badPeerIPs[ip.String()] = struct{}{} } -func (cl *Client) newConnection(nc net.Conn, outgoing bool, remoteAddr net.Addr, network, connString string) (c *PeerConn) { +func (cl *Client) newConnection(nc net.Conn, outgoing bool, remoteAddr PeerRemoteAddr, network, connString string) (c *PeerConn) { c = &PeerConn{ Peer: Peer{ outgoing: outgoing, @@ -1483,7 +1484,7 @@ func (cl *Client) ListenAddrs() (ret []net.Addr) { return } -func (cl *Client) onBadAccept(addr net.Addr) { +func (cl *Client) onBadAccept(addr PeerRemoteAddr) { ipa, ok := tryIpPortFromNetAddr(addr) if !ok { return diff --git a/ipport.go b/ipport.go index d65624c3..f710354a 100644 --- a/ipport.go +++ b/ipport.go @@ -56,12 +56,15 @@ func (me ipPortAddr) String() string { return net.JoinHostPort(me.IP.String(), strconv.FormatInt(int64(me.Port), 10)) } -func tryIpPortFromNetAddr(na net.Addr) (ret ipPortAddr, ok bool) { - ret.IP = addrIpOrNil(na) - if ret.IP == nil { - return +func tryIpPortFromNetAddr(addr PeerRemoteAddr) (ipPortAddr, bool) { + ok := true + host, port, err := net.SplitHostPort(addr.String()) + if err != nil { + ok = false } - ret.Port = addrPortOrZero(na) - ok = true - return + portI64, err := strconv.ParseInt(port, 10, 0) + if err != nil { + ok = false + } + return ipPortAddr{net.ParseIP(host), int(portI64)}, ok } diff --git a/peer_info.go b/peer_info.go index fdd74c52..e7b1b7c8 100644 --- a/peer_info.go +++ b/peer_info.go @@ -1,8 +1,6 @@ package torrent import ( - "net" - "github.com/anacrolix/dht/v2/krpc" "github.com/anacrolix/torrent/peer_protocol" @@ -11,7 +9,7 @@ import ( // Peer connection info, handed about publicly. type PeerInfo struct { Id [20]byte - Addr net.Addr + Addr PeerRemoteAddr Source PeerSource // Peer is known to support encryption. SupportsEncryption bool @@ -41,5 +39,6 @@ func (me *PeerInfo) FromPex(na krpc.NodeAddr, fs peer_protocol.PexPeerFlags) { } func (me PeerInfo) addr() IpPort { - return IpPort{IP: addrIpOrNil(me.Addr), Port: uint16(addrPortOrZero(me.Addr))} + ipPort, _ := tryIpPortFromNetAddr(me.Addr) + return IpPort{ipPort.IP, uint16(ipPort.Port)} } diff --git a/peerconn.go b/peerconn.go index 25d29b34..73ac8de6 100644 --- a/peerconn.go +++ b/peerconn.go @@ -42,6 +42,10 @@ type peerRequestState struct { data []byte } +type PeerRemoteAddr interface { + String() string +} + type Peer struct { // First to ensure 64-bit alignment for atomics. See #262. _stats ConnStats @@ -52,7 +56,7 @@ type Peer struct { outgoing bool network string - RemoteAddr net.Addr + RemoteAddr PeerRemoteAddr // True if the connection is operating over MSE obfuscation. headerEncrypted bool cryptoMethod mse.CryptoMethod @@ -169,7 +173,7 @@ func (cn *Peer) expectingChunks() bool { // Returns true if the connection is over IPv6. func (cn *PeerConn) ipv6() bool { - ip := addrIpOrNil(cn.RemoteAddr) + ip := cn.remoteIp() if ip.To4() != nil { return false } @@ -1626,7 +1630,8 @@ func (c *Peer) peerPriority() (peerPriority, error) { } func (c *Peer) remoteIp() net.IP { - return addrIpOrNil(c.RemoteAddr) + host, _, _ := net.SplitHostPort(c.RemoteAddr.String()) + return net.ParseIP(host) } func (c *Peer) remoteIpPort() IpPort { @@ -1642,7 +1647,7 @@ func (c *PeerConn) pexPeerFlags() pp.PexPeerFlags { if c.outgoing { f |= pp.PexOutgoingConn } - if c.RemoteAddr != nil && strings.Contains(c.RemoteAddr.Network(), "udp") { + if c.utp() { f |= pp.PexSupportsUtp } return f @@ -1650,7 +1655,7 @@ func (c *PeerConn) pexPeerFlags() pp.PexPeerFlags { // This returns the address to use if we want to dial the peer again. It incorporates the peer's // advertised listen port. -func (c *PeerConn) dialAddr() net.Addr { +func (c *PeerConn) dialAddr() PeerRemoteAddr { if !c.outgoing && c.PeerListenPort != 0 { switch addr := c.RemoteAddr.(type) { case *net.TCPAddr: diff --git a/peerconn_test.go b/peerconn_test.go index e56114d6..a6571089 100644 --- a/peerconn_test.go +++ b/peerconn_test.go @@ -159,10 +159,10 @@ func TestConnPexPeerFlags(t *testing.T) { {&PeerConn{Peer: Peer{outgoing: false, PeerPrefersEncryption: true}}, pp.PexPrefersEncryption}, {&PeerConn{Peer: Peer{outgoing: true, PeerPrefersEncryption: false}}, pp.PexOutgoingConn}, {&PeerConn{Peer: Peer{outgoing: true, PeerPrefersEncryption: true}}, pp.PexOutgoingConn | pp.PexPrefersEncryption}, - {&PeerConn{Peer: Peer{RemoteAddr: udpAddr}}, pp.PexSupportsUtp}, - {&PeerConn{Peer: Peer{RemoteAddr: udpAddr, outgoing: true}}, pp.PexOutgoingConn | pp.PexSupportsUtp}, - {&PeerConn{Peer: Peer{RemoteAddr: tcpAddr, outgoing: true}}, pp.PexOutgoingConn}, - {&PeerConn{Peer: Peer{RemoteAddr: tcpAddr}}, 0}, + {&PeerConn{Peer: Peer{RemoteAddr: udpAddr, network: udpAddr.Network()}}, pp.PexSupportsUtp}, + {&PeerConn{Peer: Peer{RemoteAddr: udpAddr, network: udpAddr.Network(), outgoing: true}}, pp.PexOutgoingConn | pp.PexSupportsUtp}, + {&PeerConn{Peer: Peer{RemoteAddr: tcpAddr, network: tcpAddr.Network(), outgoing: true}}, pp.PexOutgoingConn}, + {&PeerConn{Peer: Peer{RemoteAddr: tcpAddr, network: tcpAddr.Network()}}, 0}, } for i, tc := range testcases { f := tc.conn.pexPeerFlags() @@ -184,22 +184,22 @@ func TestConnPexEvent(t *testing.T) { }{ { pexAdd, - &PeerConn{Peer: Peer{RemoteAddr: udpAddr}}, + &PeerConn{Peer: Peer{RemoteAddr: udpAddr, network: udpAddr.Network()}}, pexEvent{pexAdd, udpAddr, pp.PexSupportsUtp}, }, { pexDrop, - &PeerConn{Peer: Peer{RemoteAddr: tcpAddr, outgoing: true, PeerListenPort: dialTcpAddr.Port}}, + &PeerConn{Peer: Peer{RemoteAddr: tcpAddr, network: tcpAddr.Network(), outgoing: true, PeerListenPort: dialTcpAddr.Port}}, pexEvent{pexDrop, tcpAddr, pp.PexOutgoingConn}, }, { pexAdd, - &PeerConn{Peer: Peer{RemoteAddr: tcpAddr, PeerListenPort: dialTcpAddr.Port}}, + &PeerConn{Peer: Peer{RemoteAddr: tcpAddr, network: tcpAddr.Network(), PeerListenPort: dialTcpAddr.Port}}, pexEvent{pexAdd, dialTcpAddr, 0}, }, { pexDrop, - &PeerConn{Peer: Peer{RemoteAddr: udpAddr, PeerListenPort: dialUdpAddr.Port}}, + &PeerConn{Peer: Peer{RemoteAddr: udpAddr, network: udpAddr.Network(), PeerListenPort: dialUdpAddr.Port}}, pexEvent{pexDrop, dialUdpAddr, pp.PexSupportsUtp}, }, } diff --git a/pex.go b/pex.go index 4771e700..83376d48 100644 --- a/pex.go +++ b/pex.go @@ -25,7 +25,7 @@ const ( // represents a single connection (t=pexAdd) or disconnection (t=pexDrop) event type pexEvent struct { t pexEventType - addr net.Addr + addr PeerRemoteAddr f pp.PexPeerFlags } @@ -45,7 +45,7 @@ func (me *pexMsgFactory) DeltaLen() int { type addrKey string // Returns the key to use to identify a given addr in the factory. -func (me *pexMsgFactory) addrKey(addr net.Addr) addrKey { +func (me *pexMsgFactory) addrKey(addr PeerRemoteAddr) addrKey { return addrKey(addr.String()) } @@ -161,12 +161,10 @@ func (me *pexMsgFactory) PexMsg() pp.PexMsg { // Convert an arbitrary torrent peer Addr into one that can be represented by the compact addr // format. -func nodeAddr(addr net.Addr) (_ krpc.NodeAddr, ok bool) { - ipport, ok := tryIpPortFromNetAddr(addr) - if !ok { - return - } - return krpc.NodeAddr{IP: shortestIP(ipport.IP), Port: ipport.Port}, true +func nodeAddr(addr PeerRemoteAddr) (krpc.NodeAddr, bool) { + ipport, _ := tryIpPortFromNetAddr(addr) + ok := ipport.IP != nil + return krpc.NodeAddr{IP: shortestIP(ipport.IP), Port: ipport.Port}, ok } // mainly for the krpc marshallers