Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
5c4540fefa | |||
49d0e283c4 | |||
33b8e86bc8 | |||
b90e1fa1c2 | |||
f2e867aac8 | |||
75cc36662f | |||
fe2211083e | |||
f61be4530f | |||
1b5592e0f8 | |||
24602b3dde | |||
a0d7ab2647 | |||
a163dcb666 |
@ -116,7 +116,6 @@ func (d *Decoder) Decode(val interface{}) error {
|
||||
if rv.Kind() != reflect.Ptr || rv.IsNil() {
|
||||
return errors.New("Unwritable type passed into decode")
|
||||
}
|
||||
|
||||
return d.decodeInto(rv)
|
||||
}
|
||||
|
||||
@ -131,6 +130,12 @@ func DecodeString(in string, val interface{}) error {
|
||||
// DecodeBytes reads the data in b and stores it into the value pointed to by val.
|
||||
// Read the docs for Decode for more information.
|
||||
func DecodeBytes(b []byte, val interface{}) error {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
r := bytes.NewReader(b)
|
||||
d := NewDecoder(r)
|
||||
return d.Decode(val)
|
||||
@ -173,7 +178,6 @@ func (d *Decoder) decodeInto(val reflect.Value) (err error) {
|
||||
textUnmarshaler encoding.TextUnmarshaler
|
||||
)
|
||||
unmarshaler, textUnmarshaler, v = d.indirect(val)
|
||||
|
||||
// if we're decoding into an Unmarshaler,
|
||||
// we pass on the next bencode value to this value instead,
|
||||
// so it can decide what to do with it.
|
||||
|
@ -120,8 +120,8 @@ func TestDecode(t *testing.T) {
|
||||
|
||||
{`d1:X3:foo1:Yi10e1:h3:bare`, new(dT), dT{"foo", 10, ""}, false, false},
|
||||
{`d3:fooli0ei1ee3:barli2ei3eee`, new(map[string][]int), map[string][]int{
|
||||
"foo": []int{0, 1},
|
||||
"bar": []int{2, 3},
|
||||
"foo": {0, 1},
|
||||
"bar": {2, 3},
|
||||
}, false, false},
|
||||
{`de`, new(map[string]string), map[string]string{}, false, false},
|
||||
|
||||
|
@ -112,8 +112,8 @@ func TestEncode(t *testing.T) {
|
||||
{"c": 2, "d": 3},
|
||||
}, `ld1:ai0e1:bi1eed1:ci2e1:di3eee`, false},
|
||||
{[][]byte{
|
||||
[]byte{'0', '2', '4', '6', '8'},
|
||||
[]byte{'a', 'c', 'e'},
|
||||
{'0', '2', '4', '6', '8'},
|
||||
{'a', 'c', 'e'},
|
||||
}, `l5:024683:acee`, false},
|
||||
{(*[]interface{})(nil), ``, false},
|
||||
|
||||
|
@ -153,7 +153,7 @@ func (bl *blacklist) Add(ip string, port int) {
|
||||
wp.Enable = false
|
||||
wp.Ports = nil
|
||||
} else if wp.Ports == nil {
|
||||
wp.Ports = map[int]struct{}{port: struct{}{}}
|
||||
wp.Ports = map[int]struct{}{port: {}}
|
||||
} else {
|
||||
wp.Ports[port] = struct{}{}
|
||||
}
|
||||
|
@ -22,9 +22,12 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
"github.com/xgfone/bt/bencode"
|
||||
"github.com/xgfone/bt/krpc"
|
||||
"github.com/xgfone/bt/metainfo"
|
||||
@ -43,6 +46,7 @@ var errUnsupportedIPProtocol = fmt.Errorf("unsupported ip protocol")
|
||||
const (
|
||||
IPv4Protocol IPProtocolStack = 4
|
||||
IPv6Protocol IPProtocolStack = 6
|
||||
I2PProtocol IPProtocolStack = 2
|
||||
)
|
||||
|
||||
// IPProtocolStack represents the ip protocol stack, such as IPv4 or IPv6
|
||||
@ -53,7 +57,7 @@ type Result struct {
|
||||
// Addr is the address of the peer where the request is sent to.
|
||||
//
|
||||
// Notice: it may be nil for "get_peers" request.
|
||||
Addr *net.UDPAddr
|
||||
Addr net.Addr
|
||||
|
||||
// For Error
|
||||
Code int // 0 represents the success.
|
||||
@ -138,14 +142,16 @@ type Config struct {
|
||||
// that's, the "get_peers" query.
|
||||
//
|
||||
// The default callback does noting.
|
||||
OnSearch func(infohash string, ip net.IP, port uint16)
|
||||
//OnSearch func(infohash string, ip net.Addr, port uint16)
|
||||
OnSearch func(infohash string, ip net.Addr) //, port uint16)
|
||||
|
||||
// OnTorrent is called when someone has the torrent infohash
|
||||
// or someone has just downloaded the torrent infohash,
|
||||
// that's, the "get_peers" response or "announce_peer" query.
|
||||
//
|
||||
// The default callback does noting.
|
||||
OnTorrent func(infohash string, ip net.IP, port uint16)
|
||||
//OnTorrent func(infohash string, ip net.Addr, port uint16)
|
||||
OnTorrent func(infohash string, ip net.Addr) //, port uint16)
|
||||
|
||||
// HandleInMessage is used to intercept the incoming DHT message.
|
||||
// For example, you can debug the message as the log.
|
||||
@ -153,7 +159,7 @@ type Config struct {
|
||||
// Return true if going on handling by the default. Or return false.
|
||||
//
|
||||
// The default is nil.
|
||||
HandleInMessage func(*net.UDPAddr, *krpc.Message) bool
|
||||
HandleInMessage func(net.Addr, *krpc.Message) bool
|
||||
|
||||
// HandleOutMessage is used to intercept the outgoing DHT message.
|
||||
// For example, you can debug the message as the log.
|
||||
@ -161,11 +167,11 @@ type Config struct {
|
||||
// Return (false, nil) if going on handling by the default.
|
||||
//
|
||||
// The default is nil.
|
||||
HandleOutMessage func(*net.UDPAddr, *krpc.Message) (wrote bool, err error)
|
||||
HandleOutMessage func(net.Addr, *krpc.Message) (wrote bool, err error)
|
||||
}
|
||||
|
||||
func (c Config) in(*net.UDPAddr, *krpc.Message) bool { return true }
|
||||
func (c Config) out(*net.UDPAddr, *krpc.Message) (bool, error) { return false, nil }
|
||||
func (c Config) in(net.Addr, *krpc.Message) bool { return true }
|
||||
func (c Config) out(net.Addr, *krpc.Message) (bool, error) { return false, nil }
|
||||
|
||||
func (c *Config) set(conf ...Config) {
|
||||
if len(conf) > 0 {
|
||||
@ -197,10 +203,10 @@ func (c *Config) set(conf ...Config) {
|
||||
c.RespTimeout = time.Second * 10
|
||||
}
|
||||
if c.OnSearch == nil {
|
||||
c.OnSearch = func(string, net.IP, uint16) {}
|
||||
c.OnSearch = func(string, net.Addr) {}
|
||||
}
|
||||
if c.OnTorrent == nil {
|
||||
c.OnTorrent = func(string, net.IP, uint16) {}
|
||||
c.OnTorrent = func(string, net.Addr) {}
|
||||
}
|
||||
if c.HandleInMessage == nil {
|
||||
c.HandleInMessage = c.in
|
||||
@ -212,42 +218,56 @@ func (c *Config) set(conf ...Config) {
|
||||
|
||||
// Server is a DHT server.
|
||||
type Server struct {
|
||||
conf Config
|
||||
exit chan struct{}
|
||||
conn net.PacketConn
|
||||
once sync.Once
|
||||
conf Config
|
||||
exit chan struct{}
|
||||
packetConn net.PacketConn
|
||||
once sync.Once
|
||||
|
||||
ipv4 bool
|
||||
ipv6 bool
|
||||
want []krpc.Want
|
||||
ipv4 bool
|
||||
ipv6 bool
|
||||
i2p bool
|
||||
i2pkeys i2pkeys.I2PKeys
|
||||
rawConn net.PacketConn
|
||||
want []krpc.Want
|
||||
|
||||
peerManager PeerManager
|
||||
routingTable4 *routingTable
|
||||
routingTable6 *routingTable
|
||||
routingTableI2P *routingTable
|
||||
tokenManager *tokenManager
|
||||
tokenPeerManager *tokenPeerManager
|
||||
transactionManager *transactionManager
|
||||
}
|
||||
|
||||
func SplitHostPort(raddr net.Addr) (string, int) {
|
||||
var host, port, _ = net.SplitHostPort(raddr.String())
|
||||
portint, _ := strconv.Atoi(port)
|
||||
return host, portint
|
||||
}
|
||||
|
||||
// NewServer returns a new DHT server.
|
||||
func NewServer(conn net.PacketConn, config ...Config) *Server {
|
||||
func NewServer(conn net.PacketConn, raw net.PacketConn, config ...Config) *Server {
|
||||
var conf Config
|
||||
conf.set(config...)
|
||||
|
||||
if len(conf.IPProtocols) == 0 {
|
||||
host, _, err := net.SplitHostPort(conn.LocalAddr().String())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else if ip := net.ParseIP(host); ipIsZero(ip) {
|
||||
conf.IPProtocols = []IPProtocolStack{IPv4Protocol, IPv6Protocol}
|
||||
} else if ip.To4() != nil {
|
||||
conf.IPProtocols = []IPProtocolStack{IPv4Protocol}
|
||||
if strings.HasSuffix(conn.LocalAddr().String(), ".i2p") {
|
||||
conf.IPProtocols = []IPProtocolStack{I2PProtocol}
|
||||
} else {
|
||||
conf.IPProtocols = []IPProtocolStack{IPv6Protocol}
|
||||
host, _, err := net.SplitHostPort(conn.LocalAddr().String())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else if ip := net.ParseIP(host); ipIsZero(&net.UDPAddr{IP: ip}) {
|
||||
conf.IPProtocols = []IPProtocolStack{IPv4Protocol, IPv6Protocol}
|
||||
} else if ip.To4() != nil {
|
||||
conf.IPProtocols = []IPProtocolStack{IPv4Protocol}
|
||||
} else {
|
||||
conf.IPProtocols = []IPProtocolStack{IPv6Protocol}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ipv4, ipv6 bool
|
||||
var ipv4, ipv6, i2p bool
|
||||
var want []krpc.Want
|
||||
for _, ip := range conf.IPProtocols {
|
||||
switch ip {
|
||||
@ -257,14 +277,18 @@ func NewServer(conn net.PacketConn, config ...Config) *Server {
|
||||
case IPv6Protocol:
|
||||
ipv6 = true
|
||||
want = append(want, krpc.WantNodes6)
|
||||
case I2PProtocol:
|
||||
i2p = true
|
||||
want = append(want, krpc.WantNodesInvisible)
|
||||
}
|
||||
}
|
||||
|
||||
s := &Server{
|
||||
ipv4: ipv4,
|
||||
ipv6: ipv6,
|
||||
i2p: i2p,
|
||||
want: want,
|
||||
conn: conn,
|
||||
packetConn: conn,
|
||||
conf: conf,
|
||||
exit: make(chan struct{}),
|
||||
peerManager: conf.PeerManager,
|
||||
@ -272,9 +296,13 @@ func NewServer(conn net.PacketConn, config ...Config) *Server {
|
||||
tokenPeerManager: newTokenPeerManager(),
|
||||
transactionManager: newTransactionManager(),
|
||||
}
|
||||
if raw != nil {
|
||||
s.rawConn = raw
|
||||
}
|
||||
|
||||
s.routingTable4 = newRoutingTable(s, false)
|
||||
s.routingTable6 = newRoutingTable(s, true)
|
||||
s.routingTable4 = newRoutingTable(s, false, false)
|
||||
s.routingTable6 = newRoutingTable(s, true, false)
|
||||
s.routingTableI2P = newRoutingTable(s, false, true)
|
||||
if s.peerManager == nil {
|
||||
s.peerManager = s.tokenPeerManager
|
||||
}
|
||||
@ -324,7 +352,7 @@ func (s *Server) Node6Num() int { return s.routingTable6.Len() }
|
||||
//
|
||||
func (s *Server) AddNode(node krpc.Node) int {
|
||||
// For IPv6
|
||||
if isIPv6(node.Addr.IP) {
|
||||
if isIPv6(node.Addr) {
|
||||
if s.ipv6 {
|
||||
return s.routingTable6.AddNode(node)
|
||||
}
|
||||
@ -339,13 +367,14 @@ func (s *Server) AddNode(node krpc.Node) int {
|
||||
return NodeNotAdded
|
||||
}
|
||||
|
||||
func (s *Server) addNode(a *net.UDPAddr, id metainfo.Hash, ro bool) (r int) {
|
||||
func (s *Server) addNode(a net.Addr, id metainfo.Hash, ro bool) (r int) {
|
||||
if ro { // BEP 43
|
||||
return NodeNotAdded
|
||||
}
|
||||
|
||||
if r = s.AddNode(krpc.NewNodeByUDPAddr(id, a)); r == NodeExistAndChanged {
|
||||
s.conf.Blacklist.Add(a.IP.String(), a.Port)
|
||||
if r = s.AddNode(krpc.NewNodeByAddr(id, a)); r == NodeExistAndChanged {
|
||||
host, port := SplitHostPort(a)
|
||||
s.conf.Blacklist.Add(host, port)
|
||||
}
|
||||
|
||||
return
|
||||
@ -375,30 +404,32 @@ func (s *Server) Close() { s.once.Do(s.stop) }
|
||||
func (s *Server) Sync() {
|
||||
s.routingTable4.Sync()
|
||||
s.routingTable6.Sync()
|
||||
s.routingTableI2P.Sync()
|
||||
}
|
||||
|
||||
// Run starts the DHT server.
|
||||
func (s *Server) Run() {
|
||||
go s.routingTable4.Start(time.Minute * 5)
|
||||
go s.routingTable6.Start(time.Minute * 5)
|
||||
go s.routingTableI2P.Start(time.Minute * 5)
|
||||
go s.tokenManager.Start(time.Minute * 10)
|
||||
go s.tokenPeerManager.Start(time.Hour * 24)
|
||||
go s.transactionManager.Start(s, s.conf.RespTimeout)
|
||||
|
||||
buf := make([]byte, s.conf.MsgSize)
|
||||
for {
|
||||
n, raddr, err := s.conn.ReadFrom(buf)
|
||||
n, raddr, err := s.packetConn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
s.conf.ErrorLog("fail to read the dht message: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
s.handlePacket(raddr.(*net.UDPAddr), buf[:n])
|
||||
s.handlePacket(raddr, buf[:n])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) isDisabled(raddr *net.UDPAddr) bool {
|
||||
if isIPv6(raddr.IP) {
|
||||
func (s *Server) isDisabled(raddr net.Addr) bool {
|
||||
if isIPv6(raddr) {
|
||||
if !s.ipv6 {
|
||||
return true
|
||||
}
|
||||
@ -409,13 +440,14 @@ func (s *Server) isDisabled(raddr *net.UDPAddr) bool {
|
||||
}
|
||||
|
||||
// HandlePacket handles the incoming DHT message.
|
||||
func (s *Server) handlePacket(raddr *net.UDPAddr, data []byte) {
|
||||
func (s *Server) handlePacket(raddr net.Addr, data []byte) {
|
||||
if s.isDisabled(raddr) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether the raddr is in the ip blacklist. If yes, discard it.
|
||||
if s.conf.Blacklist.In(raddr.IP.String(), raddr.Port) {
|
||||
var host, port = SplitHostPort(raddr)
|
||||
if s.conf.Blacklist.In(host, port) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -432,7 +464,7 @@ func (s *Server) handlePacket(raddr *net.UDPAddr, data []byte) {
|
||||
go s.handleMessage(raddr, msg)
|
||||
}
|
||||
|
||||
func (s *Server) handleMessage(raddr *net.UDPAddr, m krpc.Message) {
|
||||
func (s *Server) handleMessage(raddr net.Addr, m krpc.Message) {
|
||||
if !s.conf.HandleInMessage(raddr, &m) {
|
||||
return
|
||||
}
|
||||
@ -464,7 +496,8 @@ func (s *Server) handleMessage(raddr *net.UDPAddr, m krpc.Message) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
||||
//func (s *Server) handleQuery(raddr net.Addr, m krpc.Message) {
|
||||
func (s *Server) handleQuery(raddr net.Addr, m krpc.Message) {
|
||||
switch m.Q {
|
||||
case queryMethodPing:
|
||||
s.reply(raddr, m.T, krpc.ResponseResult{})
|
||||
@ -472,8 +505,9 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
||||
var r krpc.ResponseResult
|
||||
n4 := m.A.ContainsWant(krpc.WantNodes)
|
||||
n6 := m.A.ContainsWant(krpc.WantNodes6)
|
||||
if !n4 && !n6 {
|
||||
if isIPv6(raddr.IP) {
|
||||
ni := m.A.ContainsWant(krpc.WantNodesInvisible)
|
||||
if !n4 && !n6 && !ni {
|
||||
if isIPv6(raddr) {
|
||||
r.Nodes6 = s.routingTable6.Closest(m.A.InfoHash, s.conf.K)
|
||||
} else {
|
||||
r.Nodes = s.routingTable4.Closest(m.A.InfoHash, s.conf.K)
|
||||
@ -485,23 +519,35 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
||||
if n6 {
|
||||
r.Nodes6 = s.routingTable6.Closest(m.A.InfoHash, s.conf.K)
|
||||
}
|
||||
if ni {
|
||||
r.Nodes = s.routingTableI2P.Closest(m.A.InfoHash, s.conf.K)
|
||||
}
|
||||
}
|
||||
s.reply(raddr, m.T, r)
|
||||
case queryMethodGetPeers: // See BEP 32
|
||||
n4 := m.A.ContainsWant(krpc.WantNodes)
|
||||
n6 := m.A.ContainsWant(krpc.WantNodes6)
|
||||
|
||||
ni := m.A.ContainsWant(krpc.WantNodesInvisible)
|
||||
// Get the ipv4/ipv6 peers storing the torrent infohash.
|
||||
var r krpc.ResponseResult
|
||||
if !n4 && !n6 {
|
||||
r.Values = s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, isIPv6(raddr.IP))
|
||||
if !n4 && !n6 && !ni {
|
||||
r.Values = s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, isIPv6(raddr), isI2P(raddr))
|
||||
} else {
|
||||
if n4 {
|
||||
r.Values = s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, false)
|
||||
r.Values = s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, false, false)
|
||||
}
|
||||
|
||||
if n6 {
|
||||
values := s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, true)
|
||||
values := s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, true, false)
|
||||
if len(r.Values) == 0 {
|
||||
r.Values = values
|
||||
} else {
|
||||
r.Values = append(r.Values, values...)
|
||||
}
|
||||
}
|
||||
|
||||
if ni {
|
||||
values := s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, false, true)
|
||||
if len(r.Values) == 0 {
|
||||
r.Values = values
|
||||
} else {
|
||||
@ -513,7 +559,7 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
||||
// No Peers, and return the closest other nodes.
|
||||
if len(r.Values) == 0 {
|
||||
if !n4 && !n6 {
|
||||
if isIPv6(raddr.IP) {
|
||||
if isIPv6(raddr) {
|
||||
r.Nodes6 = s.routingTable6.Closest(m.A.InfoHash, s.conf.K)
|
||||
} else {
|
||||
r.Nodes = s.routingTable4.Closest(m.A.InfoHash, s.conf.K)
|
||||
@ -530,19 +576,19 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
||||
|
||||
r.Token = s.tokenManager.Token(raddr)
|
||||
s.reply(raddr, m.T, r)
|
||||
s.conf.OnSearch(m.A.InfoHash.HexString(), raddr.IP, uint16(raddr.Port))
|
||||
s.conf.OnSearch(m.A.InfoHash.HexString(), raddr) //, uint16(port))
|
||||
case queryMethodAnnouncePeer:
|
||||
if s.tokenManager.Check(raddr, m.A.Token) {
|
||||
return
|
||||
}
|
||||
s.reply(raddr, m.T, krpc.ResponseResult{})
|
||||
s.conf.OnTorrent(m.A.InfoHash.HexString(), raddr.IP, m.A.GetPort(raddr.Port))
|
||||
s.conf.OnTorrent(m.A.InfoHash.HexString(), raddr) //, m.A.GetPort(port))
|
||||
default:
|
||||
s.sendError(raddr, m.T, "unknown query method", krpc.ErrorCodeMethodUnknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) send(raddr *net.UDPAddr, m krpc.Message) (wrote bool, err error) {
|
||||
func (s *Server) send(raddr net.Addr, m krpc.Message) (wrote bool, err error) {
|
||||
// // TODO: Should we check the ip blacklist??
|
||||
// if s.conf.Blacklist.In(raddr.IP.String(), raddr.Port) {
|
||||
// return
|
||||
@ -556,7 +602,8 @@ func (s *Server) send(raddr *net.UDPAddr, m krpc.Message) (wrote bool, err error
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Server) _send(raddr *net.UDPAddr, m krpc.Message) (wrote bool, err error) {
|
||||
func (s *Server) _send(raddr net.Addr, m krpc.Message) (bool, error) {
|
||||
var err error
|
||||
if m.T == "" || m.Y == "" {
|
||||
panic(`DHT message "t" or "y" must not be empty`)
|
||||
}
|
||||
@ -566,29 +613,40 @@ func (s *Server) _send(raddr *net.UDPAddr, m krpc.Message) (wrote bool, err erro
|
||||
if err = bencode.NewEncoder(buf).Encode(m); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var n int
|
||||
|
||||
n, err := s.conn.WriteTo(buf.Bytes(), raddr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error writing %d bytes to %s: %s", buf.Len(), raddr, err)
|
||||
s.conf.Blacklist.Add(raddr.IP.String(), 0)
|
||||
return
|
||||
if s.i2p {
|
||||
n, err = s.rawConn.WriteTo(buf.Bytes(), raddr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error writing %d bytes to %s: %s", buf.Len(), raddr, err)
|
||||
host, _, _ := net.SplitHostPort(raddr.String())
|
||||
s.conf.Blacklist.Add(host, 0)
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
n, err = s.packetConn.WriteTo(buf.Bytes(), raddr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error writing %d bytes to %s: %s", buf.Len(), raddr, err)
|
||||
host, _, _ := net.SplitHostPort(raddr.String())
|
||||
s.conf.Blacklist.Add(host, 0)
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
wrote = true
|
||||
if n != buf.Len() {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
|
||||
return
|
||||
return true, err
|
||||
}
|
||||
|
||||
func (s *Server) sendError(raddr *net.UDPAddr, tid, reason string, code int) {
|
||||
func (s *Server) sendError(raddr net.Addr, tid, reason string, code int) {
|
||||
if _, err := s.send(raddr, krpc.NewErrorMsg(tid, code, reason)); err != nil {
|
||||
s.conf.ErrorLog("error replying to %s: %s", raddr.String(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) reply(raddr *net.UDPAddr, tid string, r krpc.ResponseResult) {
|
||||
func (s *Server) reply(raddr net.Addr, tid string, r krpc.ResponseResult) {
|
||||
r.ID = s.conf.ID
|
||||
if _, err := s.send(raddr, krpc.NewResponseMsg(tid, r)); err != nil {
|
||||
s.conf.ErrorLog("error replying to %s: %s", raddr.String(), err.Error())
|
||||
@ -625,11 +683,11 @@ func (s *Server) onTimeout(t *transaction) {
|
||||
t.ID, t.Query, t.Addr.String())
|
||||
}
|
||||
|
||||
func (s *Server) onPingResp(t *transaction, a *net.UDPAddr, m krpc.Message) {
|
||||
func (s *Server) onPingResp(t *transaction, a net.Addr, m krpc.Message) {
|
||||
t.Done(Result{})
|
||||
}
|
||||
|
||||
func (s *Server) onGetPeersResp(t *transaction, a *net.UDPAddr, m krpc.Message) {
|
||||
func (s *Server) onGetPeersResp(t *transaction, a net.Addr, m krpc.Message) {
|
||||
// Store the response node with the token.
|
||||
if m.R.Token != "" {
|
||||
s.tokenPeerManager.Set(m.R.ID, a, m.R.Token)
|
||||
@ -639,7 +697,7 @@ func (s *Server) onGetPeersResp(t *transaction, a *net.UDPAddr, m krpc.Message)
|
||||
if len(m.R.Values) > 0 {
|
||||
t.Done(Result{Peers: m.R.Values})
|
||||
for _, addr := range m.R.Values {
|
||||
s.conf.OnTorrent(t.Arg.InfoHash.HexString(), addr.IP, addr.Port)
|
||||
s.conf.OnTorrent(t.Arg.InfoHash.HexString(), addr) //, uint16(addr.Port()))
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -703,7 +761,7 @@ func (s *Server) getPeers(info metainfo.Hash, addr metainfo.Address, depth int,
|
||||
|
||||
// Ping sends a PING query to addr, and the callback function cb will be called
|
||||
// when the response or error is returned, or it's timeout.
|
||||
func (s *Server) Ping(addr *net.UDPAddr, cb ...func(Result)) (err error) {
|
||||
func (s *Server) Ping(addr net.Addr, cb ...func(Result)) (err error) {
|
||||
t := newTransaction(s, addr, queryMethodPing, krpc.QueryArg{}, cb...)
|
||||
t.OnResponse = s.onPingResp
|
||||
return s.request(t)
|
||||
@ -719,7 +777,6 @@ func (s *Server) GetPeers(infohash metainfo.Hash, cb ...func(Result)) {
|
||||
if infohash.IsZero() {
|
||||
panic("the infohash of the torrent is ZERO")
|
||||
}
|
||||
|
||||
var nodes []krpc.Node
|
||||
if s.ipv4 {
|
||||
nodes = s.routingTable4.Closest(infohash, s.conf.K)
|
||||
@ -743,7 +800,6 @@ func (s *Server) GetPeers(infohash metainfo.Hash, cb ...func(Result)) {
|
||||
for _, node := range nodes {
|
||||
s.getPeers(infohash, node.Addr, s.conf.SearchDepth, ids, cb...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// AnnouncePeer announces the torrent infohash to the K closest nodes,
|
||||
@ -784,7 +840,7 @@ func (s *Server) AnnouncePeer(infohash metainfo.Hash, port uint16, impliedPort b
|
||||
// FindNode sends the "find_node" query to the addr to find the target node.
|
||||
//
|
||||
// Notice: In general, it's used to bootstrap the routing table.
|
||||
func (s *Server) FindNode(addr *net.UDPAddr, target metainfo.Hash) error {
|
||||
func (s *Server) FindNode(addr net.Addr, target metainfo.Hash) error {
|
||||
if target.IsZero() {
|
||||
panic("the target is ZERO")
|
||||
}
|
||||
@ -792,7 +848,7 @@ func (s *Server) FindNode(addr *net.UDPAddr, target metainfo.Hash) error {
|
||||
return s.findNode(target, addr, s.conf.SearchDepth, nil)
|
||||
}
|
||||
|
||||
func (s *Server) findNode(target metainfo.Hash, addr *net.UDPAddr, depth int,
|
||||
func (s *Server) findNode(target metainfo.Hash, addr net.Addr, depth int,
|
||||
ids metainfo.Hashes) error {
|
||||
arg := krpc.QueryArg{Target: target, Wants: s.want}
|
||||
t := newTransaction(s, addr, queryMethodFindNode, arg)
|
||||
@ -801,7 +857,7 @@ func (s *Server) findNode(target metainfo.Hash, addr *net.UDPAddr, depth int,
|
||||
return s.request(t)
|
||||
}
|
||||
|
||||
func (s *Server) onFindNodeResp(t *transaction, a *net.UDPAddr, m krpc.Message) {
|
||||
func (s *Server) onFindNodeResp(t *transaction, a net.Addr, m krpc.Message) {
|
||||
// Search the target node recursively.
|
||||
t.Depth--
|
||||
if t.Depth < 1 {
|
||||
@ -849,15 +905,27 @@ func (s *Server) onFindNodeResp(t *transaction, a *net.UDPAddr, m krpc.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func isIPv6(ip net.IP) bool {
|
||||
if ip.To4() == nil {
|
||||
func isIPv6(ip net.Addr) bool {
|
||||
rawip := net.ParseIP(ip.String())
|
||||
if rawip == nil {
|
||||
return false
|
||||
}
|
||||
if rawip.To4() == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ipIsZero(ip net.IP) bool {
|
||||
for _, b := range ip {
|
||||
func isI2P(ip net.Addr) bool {
|
||||
if ip.Network() == "I2P" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ipIsZero(ip net.Addr) bool {
|
||||
rawip := net.ParseIP(ip.String())
|
||||
for _, b := range rawip {
|
||||
if b != 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ package dht
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -49,7 +48,7 @@ func (pm *testPeerManager) AddPeer(infohash metainfo.Hash, addr metainfo.Address
|
||||
}
|
||||
|
||||
func (pm *testPeerManager) GetPeers(infohash metainfo.Hash, maxnum int,
|
||||
ipv6 bool) (addrs []metainfo.Address) {
|
||||
ipv6, i2p bool) (addrs []metainfo.Address) {
|
||||
// We only supports IPv4, so ignore the ipv6 argument.
|
||||
pm.lock.RLock()
|
||||
_addrs := pm.peers[infohash]
|
||||
@ -63,21 +62,21 @@ func (pm *testPeerManager) GetPeers(infohash metainfo.Hash, maxnum int,
|
||||
return
|
||||
}
|
||||
|
||||
func onSearch(infohash string, ip net.IP, port uint16) {
|
||||
addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
||||
fmt.Printf("%s is searching %s\n", addr, infohash)
|
||||
func onSearch(infohash string, ip net.Addr) {
|
||||
// addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
||||
fmt.Printf("%s is searching %s\n", ip.String(), infohash)
|
||||
}
|
||||
|
||||
func onTorrent(infohash string, ip net.IP, port uint16) {
|
||||
addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
||||
fmt.Printf("%s has downloaded %s\n", addr, infohash)
|
||||
func onTorrent(infohash string, ip net.Addr) {
|
||||
// addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
||||
fmt.Printf("%s has downloaded %s\n", ip.String(), infohash)
|
||||
}
|
||||
|
||||
func newDHTServer(id metainfo.Hash, addr string, pm PeerManager) (s *Server, err error) {
|
||||
conn, err := net.ListenPacket("udp", addr)
|
||||
if err == nil {
|
||||
c := Config{ID: id, PeerManager: pm, OnSearch: onSearch, OnTorrent: onTorrent}
|
||||
s = NewServer(conn, c)
|
||||
s = NewServer(conn, conn, c)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -149,7 +148,7 @@ func ExampleServer() {
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
// Add the peer to let the DHT server1 has the peer.
|
||||
pm.AddPeer(infohash, metainfo.NewAddress(net.ParseIP("127.0.0.1"), 9001))
|
||||
pm.AddPeer(infohash, metainfo.NewAddress(&net.UDPAddr{IP: net.ParseIP("127.0.0.1")}, 9001))
|
||||
|
||||
// Search the torrent infohash again, but from DHT server2,
|
||||
// which will search the DHT server1 recursively.
|
||||
@ -178,5 +177,9 @@ func ExampleServer() {
|
||||
// 127.0.0.1:9002 is searching 0102030405060708090a0b0c0d0e0f1011121314
|
||||
// no peers for 0102030405060708090a0b0c0d0e0f1011121314
|
||||
// 0102030405060708090a0b0c0d0e0f1011121314: 127.0.0.1:9001
|
||||
// 0102030405060708090a0b0c0d0e0f1011121314: 127.0.0.1:9001
|
||||
// 0102030405060708090a0b0c0d0e0f1011121314: 127.0.0.1:9001
|
||||
// 127.0.0.1:9001 has downloaded 0102030405060708090a0b0c0d0e0f1011121314
|
||||
// 127.0.0.1:9001 has downloaded 0102030405060708090a0b0c0d0e0f1011121314
|
||||
// 127.0.0.1:9001 has downloaded 0102030405060708090a0b0c0d0e0f1011121314
|
||||
}
|
||||
|
@ -19,18 +19,19 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
"github.com/xgfone/bt/metainfo"
|
||||
)
|
||||
|
||||
// PeerManager is used to manage the peers.
|
||||
type PeerManager interface {
|
||||
// If ipv6 is true, only return ipv6 addresses. Or return ipv4 addresses.
|
||||
GetPeers(infohash metainfo.Hash, maxnum int, ipv6 bool) []metainfo.Address
|
||||
GetPeers(infohash metainfo.Hash, maxnum int, ipv6, i2p bool) []metainfo.Address
|
||||
}
|
||||
|
||||
type peer struct {
|
||||
ID metainfo.Hash
|
||||
IP net.IP
|
||||
Addr net.Addr
|
||||
Port uint16
|
||||
Token string
|
||||
Time time.Time
|
||||
@ -75,7 +76,7 @@ func (tpm *tokenPeerManager) Start(interval time.Duration) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tpm *tokenPeerManager) Set(id metainfo.Hash, addr *net.UDPAddr, token string) {
|
||||
func (tpm *tokenPeerManager) Set(id metainfo.Hash, addr net.Addr, token string) {
|
||||
addrkey := addr.String()
|
||||
tpm.lock.Lock()
|
||||
peers, ok := tpm.peers[id]
|
||||
@ -83,10 +84,11 @@ func (tpm *tokenPeerManager) Set(id metainfo.Hash, addr *net.UDPAddr, token stri
|
||||
peers = make(map[string]peer, 4)
|
||||
tpm.peers[id] = peers
|
||||
}
|
||||
host, port := SplitHostPort(addr)
|
||||
peers[addrkey] = peer{
|
||||
ID: id,
|
||||
IP: addr.IP,
|
||||
Port: uint16(addr.Port),
|
||||
Addr: &net.UDPAddr{IP: net.ParseIP(host)},
|
||||
Port: uint16(port),
|
||||
Token: token,
|
||||
Time: time.Now(),
|
||||
}
|
||||
@ -114,7 +116,7 @@ func (tpm *tokenPeerManager) Stop() {
|
||||
}
|
||||
|
||||
func (tpm *tokenPeerManager) GetPeers(infohash metainfo.Hash, maxnum int,
|
||||
ipv6 bool) (addrs []metainfo.Address) {
|
||||
ipv6, i2p bool) (addrs []metainfo.Address) {
|
||||
addrs = make([]metainfo.Address, 0, maxnum)
|
||||
tpm.lock.RLock()
|
||||
if peers, ok := tpm.peers[infohash]; ok {
|
||||
@ -123,14 +125,21 @@ func (tpm *tokenPeerManager) GetPeers(infohash metainfo.Hash, maxnum int,
|
||||
break
|
||||
}
|
||||
|
||||
if ipv6 { // For IPv6
|
||||
if isIPv6(peer.IP) {
|
||||
if i2p {
|
||||
if isI2P(peer.Addr) {
|
||||
maxnum--
|
||||
addrs = append(addrs, metainfo.NewAddress(peer.IP, peer.Port))
|
||||
addrs = append(addrs, metainfo.NewAddress(peer.Addr.(*i2pkeys.I2PAddr), peer.Port))
|
||||
}
|
||||
} else if !isIPv6(peer.IP) { // For IPv4
|
||||
}
|
||||
|
||||
if ipv6 { // For IPv6
|
||||
if isIPv6(peer.Addr) {
|
||||
maxnum--
|
||||
addrs = append(addrs, metainfo.NewAddress(peer.Addr.(*net.UDPAddr), peer.Port))
|
||||
}
|
||||
} else if !isIPv6(peer.Addr) { // For IPv4
|
||||
maxnum--
|
||||
addrs = append(addrs, metainfo.NewAddress(peer.IP, peer.Port))
|
||||
addrs = append(addrs, metainfo.NewAddress(peer.Addr.(*net.UDPAddr), peer.Port))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ type routingTable struct {
|
||||
k int
|
||||
s *Server
|
||||
ipv6 bool
|
||||
i2p bool
|
||||
sync chan struct{}
|
||||
exit chan struct{}
|
||||
root metainfo.Hash
|
||||
@ -41,11 +42,12 @@ type routingTable struct {
|
||||
bkts []*bucket
|
||||
}
|
||||
|
||||
func newRoutingTable(s *Server, ipv6 bool) *routingTable {
|
||||
func newRoutingTable(s *Server, ipv6, i2p bool) *routingTable {
|
||||
rt := &routingTable{
|
||||
k: s.conf.K,
|
||||
s: s,
|
||||
ipv6: ipv6,
|
||||
i2p: i2p,
|
||||
root: s.conf.ID,
|
||||
sync: make(chan struct{}),
|
||||
exit: make(chan struct{}),
|
||||
@ -57,9 +59,9 @@ func newRoutingTable(s *Server, ipv6 bool) *routingTable {
|
||||
}
|
||||
|
||||
// Load all the nodes from the storage.
|
||||
nodes, err := s.conf.RoutingTableStorage.Load(s.conf.ID, ipv6)
|
||||
nodes, err := s.conf.RoutingTableStorage.Load(s.conf.ID, ipv6, i2p)
|
||||
if err != nil {
|
||||
s.conf.ErrorLog("fail to load routing table(ipv6=%v): %s", ipv6, err)
|
||||
s.conf.ErrorLog("fail to load routing table(ipv6=%v, i2p=%v): %s", ipv6, i2p, err)
|
||||
} else {
|
||||
now := time.Now()
|
||||
for _, node := range nodes {
|
||||
|
@ -29,7 +29,7 @@ type RoutingTableNode struct {
|
||||
|
||||
// RoutingTableStorage is used to store the nodes in the routing table.
|
||||
type RoutingTableStorage interface {
|
||||
Load(ownid metainfo.Hash, ipv6 bool) (nodes []RoutingTableNode, err error)
|
||||
Load(ownid metainfo.Hash, ipv6, i2p bool) (nodes []RoutingTableNode, err error)
|
||||
Dump(ownid metainfo.Hash, nodes []RoutingTableNode, ipv6 bool) (err error)
|
||||
}
|
||||
|
||||
@ -38,5 +38,5 @@ func NewNoopRoutingTableStorage() RoutingTableStorage { return noopStorage{} }
|
||||
|
||||
type noopStorage struct{}
|
||||
|
||||
func (s noopStorage) Load(metainfo.Hash, bool) (nodes []RoutingTableNode, err error) { return }
|
||||
func (s noopStorage) Dump(metainfo.Hash, []RoutingTableNode, bool) (err error) { return }
|
||||
func (s noopStorage) Load(metainfo.Hash, bool, bool) (nodes []RoutingTableNode, err error) { return }
|
||||
func (s noopStorage) Dump(metainfo.Hash, []RoutingTableNode, bool) (err error) { return }
|
||||
|
@ -83,7 +83,7 @@ func (tm *tokenManager) Stop() {
|
||||
}
|
||||
|
||||
// Token allocates a token for a node addr and returns the token.
|
||||
func (tm *tokenManager) Token(addr *net.UDPAddr) (token string) {
|
||||
func (tm *tokenManager) Token(addr net.Addr) (token string) {
|
||||
addrs := addr.String()
|
||||
tm.lock.RLock()
|
||||
token = tm.new
|
||||
@ -94,7 +94,7 @@ func (tm *tokenManager) Token(addr *net.UDPAddr) (token string) {
|
||||
|
||||
// Check checks whether the token associated with the node addr is valid,
|
||||
// that's, it's not expired.
|
||||
func (tm *tokenManager) Check(addr *net.UDPAddr, token string) (ok bool) {
|
||||
func (tm *tokenManager) Check(addr net.Addr, token string) (ok bool) {
|
||||
tm.lock.RLock()
|
||||
last, new := tm.last, tm.new
|
||||
tm.lock.RUnlock()
|
||||
|
@ -29,7 +29,7 @@ type transaction struct {
|
||||
ID string
|
||||
Query string
|
||||
Arg krpc.QueryArg
|
||||
Addr *net.UDPAddr
|
||||
Addr net.Addr
|
||||
Time time.Time
|
||||
Depth int
|
||||
|
||||
@ -37,7 +37,7 @@ type transaction struct {
|
||||
Callback func(Result)
|
||||
OnError func(t *transaction, code int, reason string)
|
||||
OnTimeout func(t *transaction)
|
||||
OnResponse func(t *transaction, radd *net.UDPAddr, msg krpc.Message)
|
||||
OnResponse func(t *transaction, radd net.Addr, msg krpc.Message)
|
||||
}
|
||||
|
||||
func (t *transaction) Done(r Result) {
|
||||
@ -47,8 +47,8 @@ func (t *transaction) Done(r Result) {
|
||||
}
|
||||
}
|
||||
|
||||
func noopResponse(*transaction, *net.UDPAddr, krpc.Message) {}
|
||||
func newTransaction(s *Server, a *net.UDPAddr, q string, qa krpc.QueryArg,
|
||||
func noopResponse(*transaction, net.Addr, krpc.Message) {}
|
||||
func newTransaction(s *Server, a net.Addr, q string, qa krpc.QueryArg,
|
||||
callback ...func(Result)) *transaction {
|
||||
var cb func(Result)
|
||||
if len(callback) > 0 {
|
||||
@ -141,7 +141,7 @@ func (tm *transactionManager) DeleteTransaction(t *transaction) {
|
||||
// and the peer address.
|
||||
//
|
||||
// Return nil if there is no the transaction.
|
||||
func (tm *transactionManager) PopTransaction(tid string, addr *net.UDPAddr) (t *transaction) {
|
||||
func (tm *transactionManager) PopTransaction(tid string, addr net.Addr) (t *transaction) {
|
||||
key := transactionkey{id: tid, addr: addr.String()}
|
||||
tm.lock.Lock()
|
||||
if t = tm.trans[key]; t != nil {
|
||||
|
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
||||
module github.com/xgfone/bt
|
||||
|
||||
go 1.11
|
||||
|
||||
require github.com/eyedeekay/sam3 v0.32.32
|
||||
|
@ -17,6 +17,7 @@ package krpc
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/xgfone/bt/bencode"
|
||||
"github.com/xgfone/bt/metainfo"
|
||||
@ -196,8 +197,9 @@ type Want string
|
||||
//
|
||||
// BEP 32
|
||||
const (
|
||||
WantNodes Want = "n4"
|
||||
WantNodes6 Want = "n6"
|
||||
WantNodes Want = "n4"
|
||||
WantNodes6 Want = "n6"
|
||||
WantNodesInvisible Want = "n4"
|
||||
)
|
||||
|
||||
// QueryArg represents the arguments used by the QUERY message.
|
||||
@ -279,6 +281,8 @@ type ResponseResult struct {
|
||||
// find_node
|
||||
Nodes6 CompactIPv6Node `bencode:"nodes6,omitempty"` // BEP 32
|
||||
|
||||
// NodesI2P CompactI2PNode `bencode:nodes,omitempty`
|
||||
|
||||
// Token is used for future "announce_peer".
|
||||
//
|
||||
// get_peers
|
||||
@ -330,12 +334,28 @@ func (cas *CompactAddresses) UnmarshalBinary(b []byte) (err error) {
|
||||
// CompactIPv4Node is a set of IPv4 Nodes.
|
||||
type CompactIPv4Node []Node
|
||||
|
||||
func To4(addr net.Addr) net.IP {
|
||||
rawip := net.ParseIP(addr.String())
|
||||
if rawip == nil {
|
||||
return nil
|
||||
}
|
||||
return rawip.To4()
|
||||
}
|
||||
|
||||
func To16(addr net.Addr) net.IP {
|
||||
rawip := net.ParseIP(addr.String())
|
||||
if rawip == nil {
|
||||
return nil
|
||||
}
|
||||
return rawip.To16()
|
||||
}
|
||||
|
||||
// MarshalBinary implements the interface binary.BinaryMarshaler.
|
||||
func (cn CompactIPv4Node) MarshalBinary() ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Grow(26 * len(cn))
|
||||
for _, ni := range cn {
|
||||
if ni.Addr.IP = ni.Addr.IP.To4(); len(ni.Addr.IP) == 0 {
|
||||
if len(To4(ni.Addr.Addr)) == 0 {
|
||||
continue
|
||||
}
|
||||
if n, err := ni.WriteBinary(buf); err != nil {
|
||||
@ -394,7 +414,7 @@ func (cn CompactIPv6Node) MarshalBinary() ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Grow(38 * len(cn))
|
||||
for _, ni := range cn {
|
||||
ni.Addr.IP = ni.Addr.IP.To16()
|
||||
//ni.Addr.Addr = ni.Addr.Addr
|
||||
if n, err := ni.WriteBinary(buf); err != nil {
|
||||
return nil, err
|
||||
} else if n != 38 {
|
||||
@ -440,3 +460,58 @@ func (cn *CompactIPv6Node) UnmarshalBencode(b []byte) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CompactI2PNode is a set of IPv6 Nodes.
|
||||
type CompactI2PNode []Node
|
||||
|
||||
// MarshalBinary implements the interface binary.BinaryMarshaler.
|
||||
func (cn CompactI2PNode) MarshalBinary() ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Grow(54 * len(cn))
|
||||
for _, ni := range cn {
|
||||
//ni.Addr.IP = ni.Addr.IP
|
||||
if n, err := ni.WriteBinary(buf); err != nil {
|
||||
return nil, err
|
||||
} else if n != 54 {
|
||||
panic(fmt.Errorf("CompactI2PNodeInfo: the invalid NodeInfo length '%d'", n))
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements the interface binary.BinaryUnmarshaler.
|
||||
func (cn *CompactI2PNode) UnmarshalBinary(b []byte) (err error) {
|
||||
_len := len(b)
|
||||
if _len%54 != 0 {
|
||||
return fmt.Errorf("CompactI2PNodeInfo: invalid bytes length '%d'", _len)
|
||||
}
|
||||
|
||||
nis := make([]Node, 0, _len/54)
|
||||
for i := 0; i < _len; i += 54 {
|
||||
var ni Node
|
||||
if err = ni.UnmarshalBinary(b[i : i+54]); err != nil {
|
||||
return
|
||||
}
|
||||
nis = append(nis, ni)
|
||||
}
|
||||
|
||||
*cn = nis
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalBencode implements the interface bencode.Marshaler.
|
||||
func (cn CompactI2PNode) MarshalBencode() (b []byte, err error) {
|
||||
if b, err = cn.MarshalBinary(); err == nil {
|
||||
b, err = bencode.EncodeBytes(b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalBencode implements the interface bencode.Unmarshaler.
|
||||
func (cn *CompactI2PNode) UnmarshalBencode(b []byte) (err error) {
|
||||
var s string
|
||||
if err = bencode.DecodeBytes(b, &s); err == nil {
|
||||
err = cn.UnmarshalBinary([]byte(s))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
24
krpc/node.go
24
krpc/node.go
@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
"github.com/xgfone/bt/metainfo"
|
||||
)
|
||||
|
||||
@ -30,8 +31,20 @@ type Node struct {
|
||||
}
|
||||
|
||||
// NewNode returns a new Node.
|
||||
func NewNode(id metainfo.Hash, ip net.IP, port int) Node {
|
||||
return Node{ID: id, Addr: metainfo.NewAddress(ip, uint16(port))}
|
||||
func NewNode(id metainfo.Hash, ip net.Addr, port uint16) Node {
|
||||
return Node{ID: id, Addr: metainfo.NewAddress(ip, port)}
|
||||
}
|
||||
|
||||
// NewNodeByUDPAddr returns a new Node with the id and the UDP address.
|
||||
func NewNodeByAddr(id metainfo.Hash, addr net.Addr) (n Node) {
|
||||
switch addr.(type) {
|
||||
case *net.UDPAddr:
|
||||
return NewNodeByUDPAddr(id, addr.(*net.UDPAddr))
|
||||
case *i2pkeys.I2PAddr:
|
||||
return NewNodeByI2PAddr(id, addr.(*i2pkeys.I2PAddr))
|
||||
default:
|
||||
return Node{}
|
||||
}
|
||||
}
|
||||
|
||||
// NewNodeByUDPAddr returns a new Node with the id and the UDP address.
|
||||
@ -41,6 +54,13 @@ func NewNodeByUDPAddr(id metainfo.Hash, addr *net.UDPAddr) (n Node) {
|
||||
return
|
||||
}
|
||||
|
||||
// NewNodeByI2PAddr returns a new Node with the id and the UDP address.
|
||||
func NewNodeByI2PAddr(id metainfo.Hash, addr *i2pkeys.I2PAddr) (n Node) {
|
||||
n.ID = id
|
||||
n.Addr.FromI2PAddr(addr)
|
||||
return
|
||||
}
|
||||
|
||||
func (n Node) String() string {
|
||||
return fmt.Sprintf("Node<%x@%s>", n.ID, n.Addr)
|
||||
}
|
||||
|
@ -19,9 +19,12 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
"github.com/xgfone/bt/bencode"
|
||||
)
|
||||
|
||||
@ -31,16 +34,79 @@ var ErrInvalidAddr = fmt.Errorf("invalid compact information of ip and port")
|
||||
// Address represents a client/server listening on a UDP port implementing
|
||||
// the DHT protocol.
|
||||
type Address struct {
|
||||
IP net.IP // For IPv4, its length must be 4.
|
||||
Port uint16
|
||||
Addr net.Addr
|
||||
}
|
||||
|
||||
func EmtpyAddress() Address {
|
||||
var a Address
|
||||
a.Addr = &net.UDPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 0,
|
||||
}
|
||||
//a.Port = 0
|
||||
return a
|
||||
}
|
||||
|
||||
func To4(addr net.Addr) net.IP {
|
||||
if addr == nil {
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
rawip := net.ParseIP(Host(addr))
|
||||
if rawip == nil {
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
return rawip.To4()
|
||||
}
|
||||
|
||||
func To16(addr net.Addr) net.IP {
|
||||
if addr == nil {
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
rawip := net.ParseIP(Host(addr))
|
||||
if rawip == nil {
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
return rawip.To16()
|
||||
}
|
||||
|
||||
func ToIP(addr net.Addr) net.IP {
|
||||
if addr == nil {
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
return net.ParseIP(Host(addr))
|
||||
}
|
||||
|
||||
func Host(raddr net.Addr) string {
|
||||
h, _ := SplitHostPort(raddr)
|
||||
return h
|
||||
}
|
||||
|
||||
func SplitHostPort(raddr net.Addr) (string, int) {
|
||||
var host, port, err = net.SplitHostPort(raddr.String())
|
||||
if err != nil {
|
||||
//host = net.ParseIP(raddr.String()).String()
|
||||
port = strings.Replace(raddr.String(), ":", "", -1)
|
||||
}
|
||||
if host == "" {
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
portint, _ := strconv.Atoi(port)
|
||||
return host, portint
|
||||
}
|
||||
|
||||
// NewAddress returns a new Address.
|
||||
func NewAddress(ip net.IP, port uint16) Address {
|
||||
if ipv4 := ip.To4(); len(ipv4) > 0 {
|
||||
ip = ipv4
|
||||
func NewAddress(ip net.Addr, port uint16) Address {
|
||||
addr := &Address{
|
||||
Addr: &net.UDPAddr{
|
||||
IP: ip.(*net.UDPAddr).IP,
|
||||
Port: int(port),
|
||||
},
|
||||
}
|
||||
return Address{IP: ip, Port: port}
|
||||
return *addr
|
||||
}
|
||||
|
||||
func (a Address) Network() string {
|
||||
return a.Addr.Network()
|
||||
}
|
||||
|
||||
// NewAddressFromString returns a new Address by the address string.
|
||||
@ -72,11 +138,7 @@ func NewAddressesFromString(s string) (addrs []Address, err error) {
|
||||
|
||||
addrs = make([]Address, len(ips))
|
||||
for i, ip := range ips {
|
||||
if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||
addrs[i] = Address{IP: ipv4, Port: port}
|
||||
} else {
|
||||
addrs[i] = Address{IP: ip, Port: port}
|
||||
}
|
||||
addrs[i] = Address{Addr: &net.UDPAddr{IP: ip, Port: int(port)}}
|
||||
}
|
||||
|
||||
return
|
||||
@ -89,12 +151,13 @@ func (a *Address) FromString(addr string) (err error) {
|
||||
return fmt.Errorf("invalid address '%s': %s", addr, err)
|
||||
}
|
||||
|
||||
var p int
|
||||
if port != "" {
|
||||
v, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address '%s': %s", addr, err)
|
||||
}
|
||||
a.Port = uint16(v)
|
||||
p = int(v)
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(host)
|
||||
@ -104,54 +167,79 @@ func (a *Address) FromString(addr string) (err error) {
|
||||
return fmt.Errorf("the domain '%s' has no ips", host)
|
||||
}
|
||||
|
||||
a.IP = ips[0]
|
||||
if ip := a.IP.To4(); len(ip) > 0 {
|
||||
a.IP = ip
|
||||
a.Addr = &net.UDPAddr{IP: ips[0], Port: p}
|
||||
log.Println(a)
|
||||
if ip := To4(a.Addr); len(ip) > 0 {
|
||||
a.Addr = &net.UDPAddr{IP: ip, Port: p}
|
||||
}
|
||||
|
||||
log.Println(a)
|
||||
return
|
||||
}
|
||||
|
||||
// FromUDPAddr sets the ip from net.UDPAddr.
|
||||
func (a *Address) FromUDPAddr(ua *net.UDPAddr) {
|
||||
a.Port = uint16(ua.Port)
|
||||
a.IP = ua.IP
|
||||
if ipv4 := a.IP.To4(); len(ipv4) != 0 {
|
||||
a.IP = ipv4
|
||||
}
|
||||
a.Addr = ua
|
||||
}
|
||||
|
||||
// FromI2PAddr sets the ip from net.UDPAddr.
|
||||
func (a *Address) FromI2PAddr(ua *i2pkeys.I2PAddr) {
|
||||
a.Addr = ua
|
||||
}
|
||||
|
||||
// UDPAddr creates a new net.UDPAddr.
|
||||
func (a Address) UDPAddr() *net.UDPAddr {
|
||||
return &net.UDPAddr{
|
||||
IP: a.IP,
|
||||
Port: int(a.Port),
|
||||
IP: ToIP(a.Addr),
|
||||
Port: int(a.Port()),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Address) Port() uint16 {
|
||||
switch a.Addr.(type) {
|
||||
case i2pkeys.I2PAddr:
|
||||
return 6881
|
||||
default:
|
||||
_, port := SplitHostPort(a.Addr)
|
||||
return uint16(port)
|
||||
}
|
||||
}
|
||||
|
||||
func (a Address) String() string {
|
||||
if a.Port == 0 {
|
||||
return a.IP.String()
|
||||
if a.Port() == 0 {
|
||||
host, _ := SplitHostPort(a.Addr)
|
||||
return host
|
||||
}
|
||||
return net.JoinHostPort(a.IP.String(), strconv.FormatUint(uint64(a.Port), 10))
|
||||
return a.Addr.String()
|
||||
/*host, port := SplitHostPort(a.Addr)
|
||||
if port != 0 {
|
||||
a.Port = uint16(port)
|
||||
}
|
||||
if a.Port == 0 {
|
||||
return host
|
||||
}
|
||||
r := net.JoinHostPort(host, strconv.FormatUint(uint64(a.Port), 10))*/
|
||||
//return r
|
||||
}
|
||||
|
||||
// Equal reports whether n is equal to o, which is equal to
|
||||
// n.HasIPAndPort(o.IP, o.Port)
|
||||
func (a Address) Equal(o Address) bool {
|
||||
return a.Port == o.Port && a.IP.Equal(o.IP)
|
||||
if a.String() == o.String() {
|
||||
return a.Port() == o.Port()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasIPAndPort reports whether the current node has the ip and the port.
|
||||
func (a Address) HasIPAndPort(ip net.IP, port uint16) bool {
|
||||
return port == a.Port && a.IP.Equal(ip)
|
||||
return port == a.Port() && ToIP(a.Addr).Equal(ip)
|
||||
}
|
||||
|
||||
// WriteBinary is the same as MarshalBinary, but writes the result into w
|
||||
// instead of returning.
|
||||
func (a Address) WriteBinary(w io.Writer) (m int, err error) {
|
||||
if m, err = w.Write(a.IP); err == nil {
|
||||
if err = binary.Write(w, binary.BigEndian, a.Port); err == nil {
|
||||
if m, err = w.Write([]byte(To4(a.Addr))); err == nil {
|
||||
if err = binary.Write(w, binary.BigEndian, a.Port()); err == nil {
|
||||
m += 2
|
||||
}
|
||||
}
|
||||
@ -167,15 +255,17 @@ func (a *Address) UnmarshalBinary(b []byte) (err error) {
|
||||
return ErrInvalidAddr
|
||||
}
|
||||
|
||||
a.IP = make(net.IP, _len)
|
||||
copy(a.IP, b[:_len])
|
||||
a.Port = binary.BigEndian.Uint16(b[_len:])
|
||||
ip := make(net.IP, _len)
|
||||
copy(ip, b[:_len])
|
||||
a.Addr = &net.UDPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(b[_len:]))}
|
||||
// a.Port = binary.BigEndian.Uint16(b[_len:])
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalBinary implements the interface binary.BinaryMarshaler.
|
||||
func (a Address) MarshalBinary() (data []byte, err error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
log.Println("Marshal IP", a)
|
||||
buf.Grow(20)
|
||||
if _, err = a.WriteBinary(buf); err == nil {
|
||||
data = buf.Bytes()
|
||||
@ -194,14 +284,15 @@ func (a *Address) decode(vs []interface{}) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
host := vs[0].(string)
|
||||
if a.IP = net.ParseIP(host); len(a.IP) == 0 {
|
||||
ip := net.ParseIP(vs[0].(string))
|
||||
|
||||
if len(ip) == 0 {
|
||||
return ErrInvalidAddr
|
||||
} else if ip := a.IP.To4(); len(ip) != 0 {
|
||||
a.IP = ip
|
||||
} else if ipv4 := ip.To4(); len(ipv4) > 0 {
|
||||
a.Addr = &net.UDPAddr{IP: ipv4, Port: int(vs[1].(int64))}
|
||||
}
|
||||
|
||||
a.Port = uint16(vs[1].(int64))
|
||||
//a.Port = uint16(vs[1].(int64))
|
||||
return
|
||||
}
|
||||
|
||||
@ -228,7 +319,17 @@ func (a *Address) UnmarshalBencode(b []byte) (err error) {
|
||||
func (a Address) MarshalBencode() (b []byte, err error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Grow(32)
|
||||
err = bencode.NewEncoder(buf).Encode([]interface{}{a.IP.String(), a.Port})
|
||||
switch a.Addr.(type) {
|
||||
case i2pkeys.I2PAddr:
|
||||
err = bencode.NewEncoder(buf).Encode([]interface{}{a.Addr.String(), a.Port()})
|
||||
case *net.UDPAddr:
|
||||
err = bencode.NewEncoder(buf).Encode([]interface{}{a.Addr.(*net.UDPAddr).IP.String(), a.Port()})
|
||||
case *net.IPAddr:
|
||||
err = bencode.NewEncoder(buf).Encode([]interface{}{a.Addr.(*net.IPAddr).IP.String(), a.Port()})
|
||||
default:
|
||||
err = bencode.NewEncoder(buf).Encode([]interface{}{a.String()}) //, a.Port()})
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
b = buf.Bytes()
|
||||
}
|
||||
@ -285,8 +386,8 @@ func (a HostAddress) String() string {
|
||||
|
||||
// Addresses parses the host address to a list of Addresses.
|
||||
func (a HostAddress) Addresses() (addrs []Address, err error) {
|
||||
if ip := net.ParseIP(a.Host); len(ip) != 0 {
|
||||
return []Address{NewAddress(ip, a.Port)}, nil
|
||||
if ip := net.ParseIP(a.Host); len(ip) > 0 {
|
||||
return []Address{NewAddress(&net.UDPAddr{IP: ip}, a.Port)}, nil
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(a.Host)
|
||||
@ -295,7 +396,7 @@ func (a HostAddress) Addresses() (addrs []Address, err error) {
|
||||
} else {
|
||||
addrs = make([]Address, len(ips))
|
||||
for i, ip := range ips {
|
||||
addrs[i] = NewAddress(ip, a.Port)
|
||||
addrs[i] = NewAddress(&net.UDPAddr{IP: ip}, a.Port)
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,6 +419,8 @@ func (a *HostAddress) decode(vs []interface{}) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("FUUUUUUUCK", vs)
|
||||
|
||||
a.Host = vs[0].(string)
|
||||
a.Port = uint16(vs[1].(int64))
|
||||
return
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
func TestAddress(t *testing.T) {
|
||||
var addr1 Address
|
||||
var addr1 = EmtpyAddress()
|
||||
if err := addr1.FromString("1.2.3.4:1234"); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -33,11 +33,11 @@ func TestAddress(t *testing.T) {
|
||||
t.Errorf(`expected 'l7:1.2.3.4i1234ee', but got '%s'`, s)
|
||||
}
|
||||
|
||||
var addr2 Address
|
||||
var addr2 = EmtpyAddress()
|
||||
if err = addr2.UnmarshalBencode(data); err != nil {
|
||||
t.Error(err)
|
||||
} else if addr2.String() != `1.2.3.4:1234` {
|
||||
t.Errorf("expected '1.2.3.4:1234', but got '%s'", addr2)
|
||||
t.Errorf("expected '1.2.3.4:1234', but got '%s'", &addr2)
|
||||
}
|
||||
|
||||
if data, err = addr2.MarshalBinary(); err != nil {
|
||||
|
@ -33,6 +33,7 @@ const (
|
||||
const (
|
||||
ExtendedMessageNameMetadata = "ut_metadata" // BEP 9
|
||||
ExtendedMessageNamePex = "ut_pex" // BEP 11
|
||||
ExtendedMessageNameI2P = "i2p_dht" // I2P DHT
|
||||
)
|
||||
|
||||
// Predefine some "ut_metadata" extended message types.
|
||||
@ -112,6 +113,47 @@ func (ehm ExtendedHandshakeMsg) Encode() (b []byte, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// I2PDHTExtendedMsg represents the "i2p_dht" extended message
|
||||
type I2PDHTExtendedMsg struct {
|
||||
Port int `bencode:"port"`
|
||||
RPort int `bencode:"rport"`
|
||||
|
||||
Data []byte `bencode:"-"`
|
||||
}
|
||||
|
||||
// EncodeToPayload encodes UtMetadataExtendedMsg to extended payload
|
||||
// and write the result into buf.
|
||||
func (um I2PDHTExtendedMsg) EncodeToPayload(buf *bytes.Buffer) (err error) {
|
||||
buf.Grow(len(um.Data) + 50)
|
||||
if err = bencode.NewEncoder(buf).Encode(um); err == nil {
|
||||
_, err = buf.Write(um.Data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeToBytes is equal to
|
||||
//
|
||||
// buf := new(bytes.Buffer)
|
||||
// err = um.EncodeToPayload(buf)
|
||||
// return buf.Bytes(), err
|
||||
//
|
||||
func (um I2PDHTExtendedMsg) EncodeToBytes() (b []byte, err error) {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 128))
|
||||
if err = um.EncodeToPayload(buf); err == nil {
|
||||
b = buf.Bytes()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeFromPayload decodes the extended payload to itself.
|
||||
func (um *I2PDHTExtendedMsg) DecodeFromPayload(b []byte) (err error) {
|
||||
dec := bencode.NewDecoder(bytes.NewReader(b))
|
||||
if err = dec.Decode(&um); err == nil {
|
||||
um.Data = b[dec.BytesParsed():]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UtMetadataExtendedMsg represents the "ut_metadata" extended message.
|
||||
type UtMetadataExtendedMsg struct {
|
||||
MsgType uint8 `bencode:"msg_type"` // BEP 9
|
||||
|
@ -39,14 +39,14 @@ type Peer struct {
|
||||
// Addresses returns the list of the addresses that the peer listens on.
|
||||
func (p Peer) Addresses() (addrs []metainfo.Address, err error) {
|
||||
if ip := net.ParseIP(p.IP); len(ip) != 0 {
|
||||
return []metainfo.Address{{IP: ip, Port: p.Port}}, nil
|
||||
return []metainfo.Address{{Addr: &net.UDPAddr{IP: ip, Port: int(p.Port)}}}, nil
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(p.IP)
|
||||
if _len := len(ips); err == nil && len(ips) != 0 {
|
||||
addrs = make([]metainfo.Address, _len)
|
||||
for i, ip := range ips {
|
||||
addrs[i] = metainfo.Address{IP: ip, Port: p.Port}
|
||||
addrs[i] = metainfo.Address{Addr: &net.UDPAddr{IP: ip, Port: int(p.Port)}}
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ func (ps *Peers) UnmarshalBencode(b []byte) (err error) {
|
||||
if err = addr.UnmarshalBinary([]byte(vs[i : i+6])); err != nil {
|
||||
return
|
||||
}
|
||||
peers = append(peers, Peer{IP: addr.IP.String(), Port: addr.Port})
|
||||
peers = append(peers, Peer{IP: addr.String(), Port: uint16(addr.Port())})
|
||||
}
|
||||
|
||||
*ps = peers
|
||||
@ -169,7 +169,7 @@ func (ps *Peers6) UnmarshalBencode(b []byte) (err error) {
|
||||
if err = addr.UnmarshalBinary([]byte(s[i : i+18])); err != nil {
|
||||
return
|
||||
}
|
||||
peers = append(peers, Peer{IP: addr.IP.String(), Port: addr.Port})
|
||||
peers = append(peers, Peer{IP: addr.String(), Port: uint16(addr.Port())})
|
||||
}
|
||||
|
||||
*ps = peers
|
||||
|
@ -51,7 +51,7 @@ func (testHandler) OnAnnounce(raddr *net.UDPAddr, req udptracker.AnnounceRequest
|
||||
Interval: 1,
|
||||
Leechers: 2,
|
||||
Seeders: 3,
|
||||
Addresses: []metainfo.Address{{IP: net.ParseIP("127.0.0.1"), Port: 8000}},
|
||||
Addresses: []metainfo.Address{{Addr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000}}},
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -99,8 +99,8 @@ func ExampleClient() {
|
||||
fmt.Printf("Leechers: %d\n", resp.Leechers)
|
||||
fmt.Printf("Seeders: %d\n", resp.Seeders)
|
||||
for i, addr := range resp.Addresses {
|
||||
fmt.Printf("Address[%d].IP: %s\n", i, addr.IP.String())
|
||||
fmt.Printf("Address[%d].Port: %d\n", i, addr.Port)
|
||||
fmt.Printf("Address[%d].IP: %s\n", i, addr.String())
|
||||
fmt.Printf("Address[%d].Port: %d\n", i, addr.Port())
|
||||
}
|
||||
|
||||
// Send the SCRAPE request to the UDP tracker server,
|
||||
|
@ -131,6 +131,22 @@ type AnnounceResponse struct {
|
||||
Addresses []metainfo.Address
|
||||
}
|
||||
|
||||
func To4(addr net.Addr) net.IP {
|
||||
rawip := net.ParseIP(addr.String())
|
||||
if rawip == nil {
|
||||
return nil
|
||||
}
|
||||
return rawip.To4()
|
||||
}
|
||||
|
||||
func To16(addr net.Addr) net.IP {
|
||||
rawip := net.ParseIP(addr.String())
|
||||
if rawip == nil {
|
||||
return nil
|
||||
}
|
||||
return rawip.To16()
|
||||
}
|
||||
|
||||
// EncodeTo encodes the response to buf.
|
||||
func (r AnnounceResponse) EncodeTo(buf *bytes.Buffer) {
|
||||
buf.Grow(12 + len(r.Addresses)*18)
|
||||
@ -138,10 +154,10 @@ func (r AnnounceResponse) EncodeTo(buf *bytes.Buffer) {
|
||||
binary.Write(buf, binary.BigEndian, r.Leechers)
|
||||
binary.Write(buf, binary.BigEndian, r.Seeders)
|
||||
for _, addr := range r.Addresses {
|
||||
if ip := addr.IP.To4(); ip != nil {
|
||||
if ip := To4(addr.Addr); ip != nil {
|
||||
buf.Write(ip[:])
|
||||
} else {
|
||||
buf.Write(addr.IP[:])
|
||||
buf.Write(To16(addr.Addr)[:])
|
||||
}
|
||||
binary.Write(buf, binary.BigEndian, addr.Port)
|
||||
}
|
||||
@ -166,7 +182,7 @@ func (r *AnnounceResponse) DecodeFrom(b []byte, ipv4 bool) {
|
||||
ip := make(net.IP, iplen)
|
||||
copy(ip, b[i-step:i-2])
|
||||
port := binary.BigEndian.Uint16(b[i-2 : i])
|
||||
r.Addresses = append(r.Addresses, metainfo.Address{IP: ip, Port: port})
|
||||
r.Addresses = append(r.Addresses, metainfo.Address{Addr: &net.UDPAddr{IP: ip, Port: int(port)}})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ func (testHandler) OnAnnounce(raddr *net.UDPAddr, req AnnounceRequest) (
|
||||
Interval: 1,
|
||||
Leechers: 2,
|
||||
Seeders: 3,
|
||||
Addresses: []metainfo.Address{{IP: net.ParseIP("127.0.0.1"), Port: 8001}},
|
||||
Addresses: []metainfo.Address{{Addr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port:8001 }}},
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -99,8 +99,8 @@ func ExampleClient() {
|
||||
fmt.Printf("Leechers: %d\n", resp.Leechers)
|
||||
fmt.Printf("Seeders: %d\n", resp.Seeders)
|
||||
for i, addr := range resp.Addresses {
|
||||
fmt.Printf("Address[%d].IP: %s\n", i, addr.IP.String())
|
||||
fmt.Printf("Address[%d].Port: %d\n", i, addr.Port)
|
||||
fmt.Printf("Address[%d].IP: %s\n", i, addr.String())
|
||||
fmt.Printf("Address[%d].Port: %d\n", i, addr.Port())
|
||||
}
|
||||
|
||||
// Send the SCRAPE request to the UDP tracker server,
|
||||
|
Reference in New Issue
Block a user