Compare commits
22 Commits
master
...
i2p-suppor
Author | SHA1 | Date | |
---|---|---|---|
272ef550b7 | |||
16a5d27f0c | |||
5b8308625c | |||
4e6a12c27e | |||
f33c354e82 | |||
50d4aa85a4 | |||
1487cbf781 | |||
ed906bf55c | |||
b21d3cd500 | |||
925a365e5c | |||
079c851e09 | |||
cda34d75ad | |||
3236a67c66 | |||
a8a6984ced | |||
5c1d55731f | |||
5dac4ba515 | |||
6b9883fae7 | |||
03d2419ec3 | |||
424c19af04 | |||
3c40f43f64 | |||
3c2817b87d | |||
ff4c19cc0a |
@ -102,10 +102,12 @@ func NewDecoder(r io.Reader) *Decoder {
|
|||||||
// Decode reads the bencoded value from its input and stores it in the value pointed to by val.
|
// Decode reads the bencoded value from its input and stores it in the value pointed to by val.
|
||||||
// Decode allocates maps/slices as necessary with the following additional rules:
|
// Decode allocates maps/slices as necessary with the following additional rules:
|
||||||
// To decode a bencoded value into a nil interface value, the type stored in the interface value is one of:
|
// To decode a bencoded value into a nil interface value, the type stored in the interface value is one of:
|
||||||
// int64 for bencoded integers
|
//
|
||||||
// string for bencoded strings
|
// int64 for bencoded integers
|
||||||
// []interface{} for bencoded lists
|
// string for bencoded strings
|
||||||
// map[string]interface{} for bencoded dicts
|
// []interface{} for bencoded lists
|
||||||
|
// map[string]interface{} for bencoded dicts
|
||||||
|
//
|
||||||
// To unmarshal bencode into a value implementing the Unmarshaler interface,
|
// To unmarshal bencode into a value implementing the Unmarshaler interface,
|
||||||
// Unmarshal calls that value's UnmarshalBencode method.
|
// Unmarshal calls that value's UnmarshalBencode method.
|
||||||
// Otherwise, if the value implements encoding.TextUnmarshaler
|
// Otherwise, if the value implements encoding.TextUnmarshaler
|
||||||
@ -116,7 +118,7 @@ func (d *Decoder) Decode(val interface{}) error {
|
|||||||
if rv.Kind() != reflect.Ptr || rv.IsNil() {
|
if rv.Kind() != reflect.Ptr || rv.IsNil() {
|
||||||
return errors.New("Unwritable type passed into decode")
|
return errors.New("Unwritable type passed into decode")
|
||||||
}
|
}
|
||||||
|
//log.Printf("Decoding %s", rv)
|
||||||
return d.decodeInto(rv)
|
return d.decodeInto(rv)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +339,6 @@ func (d *Decoder) decodeList(v reflect.Value) error {
|
|||||||
return fmt.Errorf("Cant store a []interface{} into %s", v.Type())
|
return fmt.Errorf("Cant store a []interface{} into %s", v.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read out the l that prefixes the list
|
// read out the l that prefixes the list
|
||||||
ch, err := d.readByte()
|
ch, err := d.readByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -529,7 +530,6 @@ func (d *Decoder) decodeDict(v reflect.Value) error {
|
|||||||
if err := d.decodeInto(subv); err != nil {
|
if err := d.decodeInto(subv); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isMap {
|
if isMap {
|
||||||
v.SetMapIndex(reflect.ValueOf(key), subv)
|
v.SetMapIndex(reflect.ValueOf(key), subv)
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ func encodeValue(w io.Writer, val reflect.Value) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Can't encode type: %s", v.Type())
|
return fmt.Errorf("can't encode type: %s", v.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
// indirectEncodeValue walks down v allocating pointers as needed,
|
// indirectEncodeValue walks down v allocating pointers as needed,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build !go1.13
|
||||||
// +build !go1.13
|
// +build !go1.13
|
||||||
|
|
||||||
package bencode
|
package bencode
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build go1.13
|
||||||
// +build go1.13
|
// +build go1.13
|
||||||
|
|
||||||
package bencode
|
package bencode
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -25,9 +25,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
"github.com/xgfone/bt/krpc"
|
"github.com/eyedeekay/go-i2p-bt/krpc"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -53,7 +54,7 @@ type Result struct {
|
|||||||
// Addr is the address of the peer where the request is sent to.
|
// Addr is the address of the peer where the request is sent to.
|
||||||
//
|
//
|
||||||
// Notice: it may be nil for "get_peers" request.
|
// Notice: it may be nil for "get_peers" request.
|
||||||
Addr *net.UDPAddr
|
Addr net.Addr
|
||||||
|
|
||||||
// For Error
|
// For Error
|
||||||
Code int // 0 represents the success.
|
Code int // 0 represents the success.
|
||||||
@ -138,14 +139,14 @@ type Config struct {
|
|||||||
// that's, the "get_peers" query.
|
// that's, the "get_peers" query.
|
||||||
//
|
//
|
||||||
// The default callback does noting.
|
// The default callback does noting.
|
||||||
OnSearch func(infohash string, ip net.IP, port uint16)
|
OnSearch func(infohash string, ip net.Addr)
|
||||||
|
|
||||||
// OnTorrent is called when someone has the torrent infohash
|
// OnTorrent is called when someone has the torrent infohash
|
||||||
// or someone has just downloaded the torrent infohash,
|
// or someone has just downloaded the torrent infohash,
|
||||||
// that's, the "get_peers" response or "announce_peer" query.
|
// that's, the "get_peers" response or "announce_peer" query.
|
||||||
//
|
//
|
||||||
// The default callback does noting.
|
// The default callback does noting.
|
||||||
OnTorrent func(infohash string, ip net.IP, port uint16)
|
OnTorrent func(infohash string, ip net.Addr)
|
||||||
|
|
||||||
// HandleInMessage is used to intercept the incoming DHT message.
|
// HandleInMessage is used to intercept the incoming DHT message.
|
||||||
// For example, you can debug the message as the log.
|
// For example, you can debug the message as the log.
|
||||||
@ -153,7 +154,7 @@ type Config struct {
|
|||||||
// Return true if going on handling by the default. Or return false.
|
// Return true if going on handling by the default. Or return false.
|
||||||
//
|
//
|
||||||
// The default is nil.
|
// 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.
|
// HandleOutMessage is used to intercept the outgoing DHT message.
|
||||||
// For example, you can debug the message as the log.
|
// For example, you can debug the message as the log.
|
||||||
@ -161,11 +162,11 @@ type Config struct {
|
|||||||
// Return (false, nil) if going on handling by the default.
|
// Return (false, nil) if going on handling by the default.
|
||||||
//
|
//
|
||||||
// The default is nil.
|
// 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) in(net.Addr, *krpc.Message) bool { return true }
|
||||||
func (c Config) out(*net.UDPAddr, *krpc.Message) (bool, error) { return false, nil }
|
func (c Config) out(net.Addr, *krpc.Message) (bool, error) { return false, nil }
|
||||||
|
|
||||||
func (c *Config) set(conf ...Config) {
|
func (c *Config) set(conf ...Config) {
|
||||||
if len(conf) > 0 {
|
if len(conf) > 0 {
|
||||||
@ -197,10 +198,10 @@ func (c *Config) set(conf ...Config) {
|
|||||||
c.RespTimeout = time.Second * 10
|
c.RespTimeout = time.Second * 10
|
||||||
}
|
}
|
||||||
if c.OnSearch == nil {
|
if c.OnSearch == nil {
|
||||||
c.OnSearch = func(string, net.IP, uint16) {}
|
c.OnSearch = func(string, net.Addr) {}
|
||||||
}
|
}
|
||||||
if c.OnTorrent == nil {
|
if c.OnTorrent == nil {
|
||||||
c.OnTorrent = func(string, net.IP, uint16) {}
|
c.OnTorrent = func(string, net.Addr) {}
|
||||||
}
|
}
|
||||||
if c.HandleInMessage == nil {
|
if c.HandleInMessage == nil {
|
||||||
c.HandleInMessage = c.in
|
c.HandleInMessage = c.in
|
||||||
@ -238,7 +239,7 @@ func NewServer(conn net.PacketConn, config ...Config) *Server {
|
|||||||
host, _, err := net.SplitHostPort(conn.LocalAddr().String())
|
host, _, err := net.SplitHostPort(conn.LocalAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else if ip := net.ParseIP(host); ipIsZero(ip) {
|
} else if ip := net.ParseIP(host); utils.IpIsZero(ip) {
|
||||||
conf.IPProtocols = []IPProtocolStack{IPv4Protocol, IPv6Protocol}
|
conf.IPProtocols = []IPProtocolStack{IPv4Protocol, IPv6Protocol}
|
||||||
} else if ip.To4() != nil {
|
} else if ip.To4() != nil {
|
||||||
conf.IPProtocols = []IPProtocolStack{IPv4Protocol}
|
conf.IPProtocols = []IPProtocolStack{IPv4Protocol}
|
||||||
@ -299,15 +300,7 @@ func (s *Server) Bootstrap(addrs []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range as {
|
for _, a := range as {
|
||||||
if isIPv6(a.IP) {
|
if err = s.FindNode(a.Addr(), s.conf.ID); err != nil {
|
||||||
if !s.ipv6 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if !s.ipv4 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = s.FindNode(a.UDPAddr(), s.conf.ID); err != nil {
|
|
||||||
s.conf.ErrorLog(`fail to bootstrap '%s': %s`, a.String(), err)
|
s.conf.ErrorLog(`fail to bootstrap '%s': %s`, a.String(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -324,15 +317,15 @@ func (s *Server) Node6Num() int { return s.routingTable6.Len() }
|
|||||||
// AddNode adds the node into the routing table.
|
// AddNode adds the node into the routing table.
|
||||||
//
|
//
|
||||||
// The returned value:
|
// The returned value:
|
||||||
// NodeAdded: The node is added successfully.
|
|
||||||
// NodeNotAdded: The node is not added and is discarded.
|
|
||||||
// NodeExistAndUpdated: The node has existed, and its status has been updated.
|
|
||||||
// NodeExistAndChanged: The node has existed, but the address is inconsistent.
|
|
||||||
// The current node will be discarded.
|
|
||||||
//
|
//
|
||||||
|
// NodeAdded: The node is added successfully.
|
||||||
|
// NodeNotAdded: The node is not added and is discarded.
|
||||||
|
// NodeExistAndUpdated: The node has existed, and its status has been updated.
|
||||||
|
// NodeExistAndChanged: The node has existed, but the address is inconsistent.
|
||||||
|
// The current node will be discarded.
|
||||||
func (s *Server) AddNode(node krpc.Node) int {
|
func (s *Server) AddNode(node krpc.Node) int {
|
||||||
// For IPv6
|
// For IPv6
|
||||||
if isIPv6(node.Addr.IP) {
|
if node.Addr.IsIPv6() {
|
||||||
if s.ipv6 {
|
if s.ipv6 {
|
||||||
return s.routingTable6.AddNode(node)
|
return s.routingTable6.AddNode(node)
|
||||||
}
|
}
|
||||||
@ -347,13 +340,13 @@ func (s *Server) AddNode(node krpc.Node) int {
|
|||||||
return NodeNotAdded
|
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
|
if ro { // BEP 43
|
||||||
return NodeNotAdded
|
return NodeNotAdded
|
||||||
}
|
}
|
||||||
|
|
||||||
if r = s.AddNode(krpc.NewNodeByUDPAddr(id, a)); r == NodeExistAndChanged {
|
if r = s.AddNode(krpc.NewNodeByUDPAddr(id, a)); r == NodeExistAndChanged {
|
||||||
s.conf.Blacklist.Add(a.IP.String(), a.Port)
|
s.conf.Blacklist.Add(utils.IPAddr(a), utils.Port(a))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -401,12 +394,12 @@ func (s *Server) Run() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.handlePacket(raddr.(*net.UDPAddr), buf[:n])
|
s.handlePacket(raddr.(net.Addr), buf[:n])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) isDisabled(raddr *net.UDPAddr) bool {
|
func (s *Server) isDisabled(raddr net.Addr) bool {
|
||||||
if isIPv6(raddr.IP) {
|
if utils.IsIPv6Addr(raddr) {
|
||||||
if !s.ipv6 {
|
if !s.ipv6 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -417,13 +410,13 @@ func (s *Server) isDisabled(raddr *net.UDPAddr) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HandlePacket handles the incoming DHT message.
|
// 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) {
|
if s.isDisabled(raddr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the raddr is in the ip blacklist. If yes, discard it.
|
// Check whether the raddr is in the ip blacklist. If yes, discard it.
|
||||||
if s.conf.Blacklist.In(raddr.IP.String(), raddr.Port) {
|
if s.conf.Blacklist.In(utils.IPAddr(raddr), utils.Port(raddr)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,7 +433,7 @@ func (s *Server) handlePacket(raddr *net.UDPAddr, data []byte) {
|
|||||||
go s.handleMessage(raddr, msg)
|
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) {
|
if !s.conf.HandleInMessage(raddr, &m) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -472,7 +465,7 @@ 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) {
|
||||||
switch m.Q {
|
switch m.Q {
|
||||||
case queryMethodPing:
|
case queryMethodPing:
|
||||||
s.reply(raddr, m.T, krpc.ResponseResult{})
|
s.reply(raddr, m.T, krpc.ResponseResult{})
|
||||||
@ -482,7 +475,7 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
|||||||
n4 := m.A.ContainsWant(krpc.WantNodes)
|
n4 := m.A.ContainsWant(krpc.WantNodes)
|
||||||
n6 := m.A.ContainsWant(krpc.WantNodes6)
|
n6 := m.A.ContainsWant(krpc.WantNodes6)
|
||||||
if !n4 && !n6 {
|
if !n4 && !n6 {
|
||||||
if isIPv6(raddr.IP) {
|
if utils.IsIPv6Addr(raddr) {
|
||||||
r.Nodes6 = s.routingTable6.Closest(m.A.InfoHash, s.conf.K)
|
r.Nodes6 = s.routingTable6.Closest(m.A.InfoHash, s.conf.K)
|
||||||
} else {
|
} else {
|
||||||
r.Nodes = s.routingTable4.Closest(m.A.InfoHash, s.conf.K)
|
r.Nodes = s.routingTable4.Closest(m.A.InfoHash, s.conf.K)
|
||||||
@ -504,7 +497,7 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
|||||||
// Get the ipv4/ipv6 peers storing the torrent infohash.
|
// Get the ipv4/ipv6 peers storing the torrent infohash.
|
||||||
var r krpc.ResponseResult
|
var r krpc.ResponseResult
|
||||||
if !n4 && !n6 {
|
if !n4 && !n6 {
|
||||||
r.Values = s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, isIPv6(raddr.IP))
|
r.Values = s.peerManager.GetPeers(m.A.InfoHash, s.conf.K, utils.IsIPv6Addr(raddr))
|
||||||
} else {
|
} else {
|
||||||
if n4 {
|
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)
|
||||||
@ -523,7 +516,7 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
|||||||
// No Peers, and return the closest other nodes.
|
// No Peers, and return the closest other nodes.
|
||||||
if len(r.Values) == 0 {
|
if len(r.Values) == 0 {
|
||||||
if !n4 && !n6 {
|
if !n4 && !n6 {
|
||||||
if isIPv6(raddr.IP) {
|
if utils.IsIPv6Addr(raddr) {
|
||||||
r.Nodes6 = s.routingTable6.Closest(m.A.InfoHash, s.conf.K)
|
r.Nodes6 = s.routingTable6.Closest(m.A.InfoHash, s.conf.K)
|
||||||
} else {
|
} else {
|
||||||
r.Nodes = s.routingTable4.Closest(m.A.InfoHash, s.conf.K)
|
r.Nodes = s.routingTable4.Closest(m.A.InfoHash, s.conf.K)
|
||||||
@ -540,23 +533,21 @@ func (s *Server) handleQuery(raddr *net.UDPAddr, m krpc.Message) {
|
|||||||
|
|
||||||
r.Token = s.tokenManager.Token(raddr)
|
r.Token = s.tokenManager.Token(raddr)
|
||||||
s.reply(raddr, m.T, r)
|
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)
|
||||||
|
|
||||||
case queryMethodAnnouncePeer:
|
case queryMethodAnnouncePeer:
|
||||||
if s.tokenManager.Check(raddr, m.A.Token) {
|
if s.tokenManager.Check(raddr, m.A.Token) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.reply(raddr, m.T, krpc.ResponseResult{})
|
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)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
s.sendError(raddr, m.T, "unknown query method", krpc.ErrorCodeMethodUnknown)
|
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??
|
// // TODO: Should we check the ip blacklist??
|
||||||
// if s.conf.Blacklist.In(raddr.IP.String(), raddr.Port) {
|
// if s.conf.Blacklist.In(utils.IPaddr(raddr), utils.Port(raddr)) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@ -568,7 +559,7 @@ func (s *Server) send(raddr *net.UDPAddr, m krpc.Message) (wrote bool, err error
|
|||||||
return
|
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) (wrote bool, err error) {
|
||||||
if m.T == "" || m.Y == "" {
|
if m.T == "" || m.Y == "" {
|
||||||
panic(`DHT message "t" or "y" must not be empty`)
|
panic(`DHT message "t" or "y" must not be empty`)
|
||||||
}
|
}
|
||||||
@ -582,7 +573,7 @@ func (s *Server) _send(raddr *net.UDPAddr, m krpc.Message) (wrote bool, err erro
|
|||||||
n, err := s.conn.WriteTo(buf.Bytes(), raddr)
|
n, err := s.conn.WriteTo(buf.Bytes(), raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("error writing %d bytes to %s: %s", buf.Len(), raddr, err)
|
err = fmt.Errorf("error writing %d bytes to %s: %s", buf.Len(), raddr, err)
|
||||||
s.conf.Blacklist.Add(raddr.IP.String(), 0)
|
s.conf.Blacklist.Add(utils.IPAddr(raddr), 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,13 +585,13 @@ func (s *Server) _send(raddr *net.UDPAddr, m krpc.Message) (wrote bool, err erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
if _, err := s.send(raddr, krpc.NewErrorMsg(tid, code, reason)); err != nil {
|
||||||
s.conf.ErrorLog("error replying to %s: %s", raddr.String(), err.Error())
|
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
|
r.ID = s.conf.ID
|
||||||
if _, err := s.send(raddr, krpc.NewResponseMsg(tid, r)); err != nil {
|
if _, err := s.send(raddr, krpc.NewResponseMsg(tid, r)); err != nil {
|
||||||
s.conf.ErrorLog("error replying to %s: %s", raddr.String(), err.Error())
|
s.conf.ErrorLog("error replying to %s: %s", raddr.String(), err.Error())
|
||||||
@ -645,11 +636,11 @@ func (s *Server) onTimeout(t *transaction) {
|
|||||||
t.ID, s.conf.ID, t.Query, qid, s.conn.LocalAddr(), t.Addr.String())
|
t.ID, s.conf.ID, t.Query, qid, s.conn.LocalAddr(), 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{})
|
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.
|
// Store the response node with the token.
|
||||||
if m.R.Token != "" {
|
if m.R.Token != "" {
|
||||||
s.tokenPeerManager.Set(m.R.ID, a, m.R.Token)
|
s.tokenPeerManager.Set(m.R.ID, a, m.R.Token)
|
||||||
@ -659,7 +650,7 @@ func (s *Server) onGetPeersResp(t *transaction, a *net.UDPAddr, m krpc.Message)
|
|||||||
if len(m.R.Values) > 0 {
|
if len(m.R.Values) > 0 {
|
||||||
t.Done(Result{Peers: m.R.Values})
|
t.Done(Result{Peers: m.R.Values})
|
||||||
for _, addr := range 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.IP)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -713,7 +704,7 @@ func (s *Server) onGetPeersResp(t *transaction, a *net.UDPAddr, m krpc.Message)
|
|||||||
func (s *Server) getPeers(info metainfo.Hash, addr metainfo.Address, depth int,
|
func (s *Server) getPeers(info metainfo.Hash, addr metainfo.Address, depth int,
|
||||||
ids metainfo.Hashes, cb ...func(Result)) {
|
ids metainfo.Hashes, cb ...func(Result)) {
|
||||||
arg := krpc.QueryArg{InfoHash: info, Wants: s.want}
|
arg := krpc.QueryArg{InfoHash: info, Wants: s.want}
|
||||||
t := newTransaction(s, addr.UDPAddr(), queryMethodGetPeers, arg, cb...)
|
t := newTransaction(s, addr.Addr(), queryMethodGetPeers, arg, cb...)
|
||||||
t.OnResponse = s.onGetPeersResp
|
t.OnResponse = s.onGetPeersResp
|
||||||
t.Depth = depth
|
t.Depth = depth
|
||||||
t.Visited = ids
|
t.Visited = ids
|
||||||
@ -724,7 +715,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
|
// 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.
|
// 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 := newTransaction(s, addr, queryMethodPing, krpc.QueryArg{}, cb...)
|
||||||
t.OnResponse = s.onPingResp
|
t.OnResponse = s.onPingResp
|
||||||
return s.request(t)
|
return s.request(t)
|
||||||
@ -783,7 +774,7 @@ func (s *Server) AnnouncePeer(infohash metainfo.Hash, port uint16, impliedPort b
|
|||||||
|
|
||||||
sentNodes := make([]krpc.Node, 0, len(nodes))
|
sentNodes := make([]krpc.Node, 0, len(nodes))
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
addr := node.Addr.UDPAddr()
|
addr := node.Addr.Addr()
|
||||||
token := s.tokenPeerManager.Get(infohash, addr)
|
token := s.tokenPeerManager.Get(infohash, addr)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
continue
|
continue
|
||||||
@ -804,7 +795,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.
|
// 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.
|
// 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() {
|
if target.IsZero() {
|
||||||
panic("the target is ZERO")
|
panic("the target is ZERO")
|
||||||
}
|
}
|
||||||
@ -812,7 +803,7 @@ func (s *Server) FindNode(addr *net.UDPAddr, target metainfo.Hash) error {
|
|||||||
return s.findNode(target, addr, s.conf.SearchDepth, nil)
|
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 {
|
ids metainfo.Hashes) error {
|
||||||
arg := krpc.QueryArg{Target: target, Wants: s.want}
|
arg := krpc.QueryArg{Target: target, Wants: s.want}
|
||||||
t := newTransaction(s, addr, queryMethodFindNode, arg)
|
t := newTransaction(s, addr, queryMethodFindNode, arg)
|
||||||
@ -821,9 +812,7 @@ func (s *Server) findNode(target metainfo.Hash, addr *net.UDPAddr, depth int,
|
|||||||
return s.request(t)
|
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) {
|
||||||
t.Done(Result{})
|
|
||||||
|
|
||||||
// Search the target node recursively.
|
// Search the target node recursively.
|
||||||
t.Depth--
|
t.Depth--
|
||||||
if t.Depth < 1 {
|
if t.Depth < 1 {
|
||||||
@ -863,26 +852,10 @@ func (s *Server) onFindNodeResp(t *transaction, a *net.UDPAddr, m krpc.Message)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
err := s.findNode(t.Arg.Target, node.Addr.UDPAddr(), t.Depth, ids)
|
err := s.findNode(t.Arg.Target, node.Addr.Addr(), t.Depth, ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.conf.ErrorLog(`fail to send "find_node" query to '%s': %s`,
|
s.conf.ErrorLog(`fail to send "find_node" query to '%s': %s`,
|
||||||
node.Addr.String(), err)
|
node.Addr.String(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isIPv6(ip net.IP) bool {
|
|
||||||
if ip.To4() == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ipIsZero(ip net.IP) bool {
|
|
||||||
for _, b := range ip {
|
|
||||||
if b != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -17,11 +17,10 @@ package dht
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testPeerManager struct {
|
type testPeerManager struct {
|
||||||
@ -63,14 +62,14 @@ func (pm *testPeerManager) GetPeers(infohash metainfo.Hash, maxnum int,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func onSearch(infohash string, ip net.IP, port uint16) {
|
func onSearch(infohash string, ip net.Addr) {
|
||||||
addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
//addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
||||||
fmt.Printf("%s is searching %s\n", addr, infohash)
|
fmt.Printf("%s is searching %s\n", ip.String(), infohash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func onTorrent(infohash string, ip net.IP, port uint16) {
|
func onTorrent(infohash string, ip net.Addr) {
|
||||||
addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
//addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10))
|
||||||
fmt.Printf("%s has downloaded %s\n", addr, infohash)
|
fmt.Printf("%s has downloaded %s\n", ip.String(), infohash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDHTServer(id metainfo.Hash, addr string, pm PeerManager) (s *Server, err error) {
|
func newDHTServer(id metainfo.Hash, addr string, pm PeerManager) (s *Server, err error) {
|
||||||
@ -178,5 +177,5 @@ func ExampleServer() {
|
|||||||
// 127.0.0.1:9002 is searching 0102030405060708090a0b0c0d0e0f1011121314
|
// 127.0.0.1:9002 is searching 0102030405060708090a0b0c0d0e0f1011121314
|
||||||
// 127.0.0.1:9003: no peers for 0102030405060708090a0b0c0d0e0f1011121314
|
// 127.0.0.1:9003: no peers for 0102030405060708090a0b0c0d0e0f1011121314
|
||||||
// 0102030405060708090a0b0c0d0e0f1011121314: 127.0.0.1:9001
|
// 0102030405060708090a0b0c0d0e0f1011121314: 127.0.0.1:9001
|
||||||
// 127.0.0.1:9001 has downloaded 0102030405060708090a0b0c0d0e0f1011121314
|
// 127.0.0.1 has downloaded 0102030405060708090a0b0c0d0e0f1011121314
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -19,7 +19,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PeerManager is used to manage the peers.
|
// PeerManager is used to manage the peers.
|
||||||
@ -29,8 +30,9 @@ type PeerManager interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type peer struct {
|
type peer struct {
|
||||||
ID metainfo.Hash
|
ID metainfo.Hash
|
||||||
IP net.IP
|
// IP net.IP
|
||||||
|
IP net.Addr
|
||||||
Port uint16
|
Port uint16
|
||||||
Token string
|
Token string
|
||||||
Time time.Time
|
Time time.Time
|
||||||
@ -75,7 +77,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()
|
addrkey := addr.String()
|
||||||
tpm.lock.Lock()
|
tpm.lock.Lock()
|
||||||
peers, ok := tpm.peers[id]
|
peers, ok := tpm.peers[id]
|
||||||
@ -85,15 +87,15 @@ func (tpm *tokenPeerManager) Set(id metainfo.Hash, addr *net.UDPAddr, token stri
|
|||||||
}
|
}
|
||||||
peers[addrkey] = peer{
|
peers[addrkey] = peer{
|
||||||
ID: id,
|
ID: id,
|
||||||
IP: addr.IP,
|
IP: addr,
|
||||||
Port: uint16(addr.Port),
|
Port: uint16(utils.Port(addr)),
|
||||||
Token: token,
|
Token: token,
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
}
|
}
|
||||||
tpm.lock.Unlock()
|
tpm.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tpm *tokenPeerManager) Get(id metainfo.Hash, addr *net.UDPAddr) (token string) {
|
func (tpm *tokenPeerManager) Get(id metainfo.Hash, addr net.Addr) (token string) {
|
||||||
addrkey := addr.String()
|
addrkey := addr.String()
|
||||||
tpm.lock.RLock()
|
tpm.lock.RLock()
|
||||||
if peers, ok := tpm.peers[id]; ok {
|
if peers, ok := tpm.peers[id]; ok {
|
||||||
@ -122,16 +124,15 @@ func (tpm *tokenPeerManager) GetPeers(infohash metainfo.Hash, maxnum int,
|
|||||||
if maxnum < 1 {
|
if maxnum < 1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
//if ipv6 { // For IPv6
|
||||||
if ipv6 { // For IPv6
|
//if isIPv6(peer.IP) {
|
||||||
if isIPv6(peer.IP) {
|
maxnum--
|
||||||
maxnum--
|
addrs = append(addrs, metainfo.NewAddress(peer.IP, peer.Port))
|
||||||
addrs = append(addrs, metainfo.NewAddress(peer.IP, peer.Port))
|
//}
|
||||||
}
|
//} else if !isIPv6(peer.IP) { // For IPv4
|
||||||
} else if !isIPv6(peer.IP) { // For IPv4
|
// maxnum--
|
||||||
maxnum--
|
// addrs = append(addrs, metainfo.NewAddress(peer.IP, peer.Port))
|
||||||
addrs = append(addrs, metainfo.NewAddress(peer.IP, peer.Port))
|
//}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tpm.lock.RUnlock()
|
tpm.lock.RUnlock()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -19,8 +19,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/krpc"
|
"github.com/eyedeekay/go-i2p-bt/krpc"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
const bktlen = 160
|
const bktlen = 160
|
||||||
@ -155,12 +155,12 @@ func (rt *routingTable) Stop() {
|
|||||||
// AddNode adds the node into the routing table.
|
// AddNode adds the node into the routing table.
|
||||||
//
|
//
|
||||||
// The returned value:
|
// The returned value:
|
||||||
// NodeAdded: The node is added successfully.
|
|
||||||
// NodeNotAdded: The node is not added and is discarded.
|
|
||||||
// NodeExistAndUpdated: The node has existed, and its status has been updated.
|
|
||||||
// NodeExistAndChanged: The node has existed, but the address is inconsistent.
|
|
||||||
// The current node will be discarded.
|
|
||||||
//
|
//
|
||||||
|
// NodeAdded: The node is added successfully.
|
||||||
|
// NodeNotAdded: The node is not added and is discarded.
|
||||||
|
// NodeExistAndUpdated: The node has existed, and its status has been updated.
|
||||||
|
// NodeExistAndChanged: The node has existed, but the address is inconsistent.
|
||||||
|
// The current node will be discarded.
|
||||||
func (rt *routingTable) AddNode(n krpc.Node) (r int) {
|
func (rt *routingTable) AddNode(n krpc.Node) (r int) {
|
||||||
if n.ID == rt.root { // Don't add itself.
|
if n.ID == rt.root { // Don't add itself.
|
||||||
return NodeNotAdded
|
return NodeNotAdded
|
||||||
@ -314,7 +314,7 @@ func (b *bucket) CheckAllNodes(now time.Time) {
|
|||||||
case nodeStatusGood:
|
case nodeStatusGood:
|
||||||
case nodeStatusDubious:
|
case nodeStatusDubious:
|
||||||
// Try to send the PING query to the dubious node to check whether it is alive.
|
// Try to send the PING query to the dubious node to check whether it is alive.
|
||||||
if err := b.table.s.Ping(node.Node.Addr.UDPAddr()); err != nil {
|
if err := b.table.s.Ping(node.Node.Addr.Addr()); err != nil {
|
||||||
b.table.s.conf.ErrorLog("fail to ping '%s': %s", node.Node.String(), err)
|
b.table.s.conf.ErrorLog("fail to ping '%s': %s", node.Node.String(), err)
|
||||||
}
|
}
|
||||||
case nodeStatusBad:
|
case nodeStatusBad:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -17,8 +17,8 @@ package dht
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/krpc"
|
"github.com/eyedeekay/go-i2p-bt/krpc"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RoutingTableNode represents the node with last changed time in the routing table.
|
// RoutingTableNode represents the node with last changed time in the routing table.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -19,7 +19,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/utils"
|
"github.com/eyedeekay/go-i2p-bt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TokenManager is used to manage and validate the token.
|
// TokenManager is used to manage and validate the token.
|
||||||
@ -83,7 +83,7 @@ func (tm *tokenManager) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Token allocates a token for a node addr and returns the token.
|
// 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()
|
addrs := addr.String()
|
||||||
tm.lock.RLock()
|
tm.lock.RLock()
|
||||||
token = tm.new
|
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,
|
// Check checks whether the token associated with the node addr is valid,
|
||||||
// that's, it's not expired.
|
// 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()
|
tm.lock.RLock()
|
||||||
last, new := tm.last, tm.new
|
last, new := tm.last, tm.new
|
||||||
tm.lock.RUnlock()
|
tm.lock.RUnlock()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -21,15 +21,15 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/krpc"
|
"github.com/eyedeekay/go-i2p-bt/krpc"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type transaction struct {
|
type transaction struct {
|
||||||
ID string
|
ID string
|
||||||
Query string
|
Query string
|
||||||
Arg krpc.QueryArg
|
Arg krpc.QueryArg
|
||||||
Addr *net.UDPAddr
|
Addr net.Addr
|
||||||
Time time.Time
|
Time time.Time
|
||||||
Depth int
|
Depth int
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ type transaction struct {
|
|||||||
Callback func(Result)
|
Callback func(Result)
|
||||||
OnError func(t *transaction, code int, reason string)
|
OnError func(t *transaction, code int, reason string)
|
||||||
OnTimeout func(t *transaction)
|
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) {
|
func (t *transaction) Done(r Result) {
|
||||||
@ -47,8 +47,8 @@ func (t *transaction) Done(r Result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func noopResponse(*transaction, *net.UDPAddr, krpc.Message) {}
|
func noopResponse(*transaction, net.Addr, krpc.Message) {}
|
||||||
func newTransaction(s *Server, a *net.UDPAddr, q string, qa krpc.QueryArg,
|
func newTransaction(s *Server, a net.Addr, q string, qa krpc.QueryArg,
|
||||||
callback ...func(Result)) *transaction {
|
callback ...func(Result)) *transaction {
|
||||||
var cb func(Result)
|
var cb func(Result)
|
||||||
if len(callback) > 0 {
|
if len(callback) > 0 {
|
||||||
@ -141,7 +141,7 @@ func (tm *transactionManager) DeleteTransaction(t *transaction) {
|
|||||||
// and the peer address.
|
// and the peer address.
|
||||||
//
|
//
|
||||||
// Return nil if there is no the transaction.
|
// 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()}
|
key := transactionkey{id: tid, addr: addr.String()}
|
||||||
tm.lock.Lock()
|
tm.lock.Lock()
|
||||||
if t = tm.trans[key]; t != nil {
|
if t = tm.trans[key]; t != nil {
|
||||||
|
92
diff.patch
Normal file
92
diff.patch
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
diff --git a/peerprotocol/extension.go b/peerprotocol/extension.go
|
||||||
|
index 7ab53ff..02eb9e3 100644
|
||||||
|
--- a/peerprotocol/extension.go
|
||||||
|
+++ b/peerprotocol/extension.go
|
||||||
|
@@ -17,6 +17,7 @@ package peerprotocol
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
+ "log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/xgfone/bt/bencode"
|
||||||
|
@@ -43,39 +44,58 @@ const (
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompactIP is used to handle the compact ipv4 or ipv6.
|
||||||
|
+//type CompactIPBytes []byte //net.IP
|
||||||
|
+
|
||||||
|
type CompactIP net.IP
|
||||||
|
|
||||||
|
+func NewCompactIP(b []byte) (CompactIP, error) {
|
||||||
|
+ if len(b) == net.IPv4len {
|
||||||
|
+ return CompactIP(b), nil
|
||||||
|
+ }
|
||||||
|
+ if len(b) == net.IPv6len {
|
||||||
|
+ return CompactIP(b), nil
|
||||||
|
+ }
|
||||||
|
+ return nil, errInvalidIP
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
func (ci CompactIP) String() string {
|
||||||
|
- return net.IP(ci).String()
|
||||||
|
+ return string(ci) //net.IP(ci).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBencode implements the interface bencode.Marshaler.
|
||||||
|
-func (ci CompactIP) MarshalBencode() ([]byte, error) {
|
||||||
|
- ip := net.IP(ci)
|
||||||
|
- if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
- ip = ipv4
|
||||||
|
+func (ci *CompactIP) MarshalBencode() ([]byte, error) {
|
||||||
|
+ ip := *ci
|
||||||
|
+ log.Println("Marshal IP,", ip, ",", len(ip), ",", ip.String(), ",", net.IPv4len)
|
||||||
|
+ log.Println("Marshal IP,", ci, ",", ",", ci.String(), ",", net.IPv4len)
|
||||||
|
+ if len(ip) == net.IPv4len {
|
||||||
|
+ ip = ip
|
||||||
|
}
|
||||||
|
return bencode.EncodeBytes(ip[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBencode implements the interface bencode.Unmarshaler.
|
||||||
|
func (ci *CompactIP) UnmarshalBencode(b []byte) (err error) {
|
||||||
|
- var ip net.IP
|
||||||
|
+ var ip []byte
|
||||||
|
if err = bencode.DecodeBytes(b, &ip); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
+ log.Println("Unmarshal IP,", ip, ",", len(ip), ",", string(ip), ",", net.IPv4len)
|
||||||
|
|
||||||
|
switch len(ip) {
|
||||||
|
case net.IPv4len, net.IPv6len:
|
||||||
|
default:
|
||||||
|
- return errInvalidIP
|
||||||
|
+ if len(ip) < net.IPv6len {
|
||||||
|
+ return errInvalidIP
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
- if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
- ip = ipv4
|
||||||
|
+ if len(ip) == net.IPv4len {
|
||||||
|
+ ip = []byte(ip)
|
||||||
|
}
|
||||||
|
+ //ip = ip
|
||||||
|
|
||||||
|
*ci = CompactIP(ip)
|
||||||
|
+ log.Println("Unmarshal IP,", ci, ",", ",", ci.String(), ",", net.IPv4len)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -90,8 +110,9 @@ type ExtendedHandshakeMsg struct {
|
||||||
|
|
||||||
|
// Port is the local client port, which is redundant and no need
|
||||||
|
// for the receiving side of the connection to send this.
|
||||||
|
- Port uint16 `bencode:"p,omitempty"` // BEP 10
|
||||||
|
- IPv6 net.IP `bencode:"ipv6,omitempty"` // BEP 10
|
||||||
|
+ Port uint16 `bencode:"p,omitempty"` // BEP 10
|
||||||
|
+ IPv6 net.IP `bencode:"ipv6,omitempty"` // BEP 10
|
||||||
|
+ //IPv6 []byte `bencode:"ipv6,omitempty"` // BEP 10
|
||||||
|
IPv4 CompactIP `bencode:"ipv4,omitempty"` // BEP 10
|
||||||
|
YourIP CompactIP `bencode:"yourip,omitempty"` // BEP 10
|
||||||
|
|
127
diff2.patch
Normal file
127
diff2.patch
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
diff --git a/peerprotocol/extension.go b/peerprotocol/extension.go
|
||||||
|
index 7ab53ff..19b7799 100644
|
||||||
|
--- a/peerprotocol/extension.go
|
||||||
|
+++ b/peerprotocol/extension.go
|
||||||
|
@@ -16,9 +16,12 @@ package peerprotocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
+ "crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
+ "log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
+ "github.com/eyedeekay/sam3/i2pkeys"
|
||||||
|
"github.com/xgfone/bt/bencode"
|
||||||
|
)
|
||||||
|
|
||||||
|
@@ -42,40 +45,83 @@ const (
|
||||||
|
UtMetadataExtendedMsgTypeReject = 2 // BEP 9
|
||||||
|
)
|
||||||
|
|
||||||
|
+const (
|
||||||
|
+ I2Plen = 32
|
||||||
|
+)
|
||||||
|
+
|
||||||
|
// CompactIP is used to handle the compact ipv4 or ipv6.
|
||||||
|
-type CompactIP net.IP
|
||||||
|
+type CompactIP []byte
|
||||||
|
+
|
||||||
|
+func NewCompactIP(addr []byte) CompactIP {
|
||||||
|
+ switch len(addr) {
|
||||||
|
+ case net.IPv4len:
|
||||||
|
+ return CompactIP(addr)
|
||||||
|
+ case net.IPv6len:
|
||||||
|
+ return CompactIP(addr)
|
||||||
|
+ case I2Plen:
|
||||||
|
+ b := sha256.Sum256(addr)
|
||||||
|
+ return CompactIP(b[:])
|
||||||
|
+ default:
|
||||||
|
+ return nil
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
|
||||||
|
func (ci CompactIP) String() string {
|
||||||
|
- return net.IP(ci).String()
|
||||||
|
+ switch len(ci) {
|
||||||
|
+ case net.IPv4len:
|
||||||
|
+ return net.IP(ci).String()
|
||||||
|
+ case net.IPv6len:
|
||||||
|
+ if ip := net.IP(ci).To4(); len(ip) != 0 {
|
||||||
|
+ return ip.String()
|
||||||
|
+ }
|
||||||
|
+ return "[" + net.IP(ci).String() + "]"
|
||||||
|
+ case I2Plen:
|
||||||
|
+ return i2pkeys.I2PAddr(ci).String()
|
||||||
|
+ default:
|
||||||
|
+ return ""
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBencode implements the interface bencode.Marshaler.
|
||||||
|
func (ci CompactIP) MarshalBencode() ([]byte, error) {
|
||||||
|
- ip := net.IP(ci)
|
||||||
|
- if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
- ip = ipv4
|
||||||
|
+ switch len(ci) {
|
||||||
|
+ case net.IPv4len:
|
||||||
|
+ ip := net.IP(ci)
|
||||||
|
+ if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
+ ip = ipv4
|
||||||
|
+ }
|
||||||
|
+ return bencode.EncodeBytes(ip[:])
|
||||||
|
+ case net.IPv6len:
|
||||||
|
+ ip := net.IP(ci)
|
||||||
|
+ if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
+ ip = ipv4
|
||||||
|
+ }
|
||||||
|
+ return bencode.EncodeBytes(ip[:])
|
||||||
|
+ case I2Plen:
|
||||||
|
+ ip := i2pkeys.I2PAddr(ci)
|
||||||
|
+ return bencode.EncodeBytes(ip[:])
|
||||||
|
+ default:
|
||||||
|
+ return nil, errInvalidIP
|
||||||
|
}
|
||||||
|
- return bencode.EncodeBytes(ip[:])
|
||||||
|
+
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBencode implements the interface bencode.Unmarshaler.
|
||||||
|
func (ci *CompactIP) UnmarshalBencode(b []byte) (err error) {
|
||||||
|
+ //var ip []byte //net.IP
|
||||||
|
var ip net.IP
|
||||||
|
if err = bencode.DecodeBytes(b, &ip); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
+ log.Println("LOG", ip)
|
||||||
|
|
||||||
|
switch len(ip) {
|
||||||
|
- case net.IPv4len, net.IPv6len:
|
||||||
|
+ case net.IPv4len, net.IPv6len, I2Plen:
|
||||||
|
default:
|
||||||
|
return errInvalidIP
|
||||||
|
}
|
||||||
|
|
||||||
|
- if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
- ip = ipv4
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- *ci = CompactIP(ip)
|
||||||
|
+ *ci = NewCompactIP(ip)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/peerprotocol/extension_test.go b/peerprotocol/extension_test.go
|
||||||
|
index ffcbdfd..d47cd7e 100644
|
||||||
|
--- a/peerprotocol/extension_test.go
|
||||||
|
+++ b/peerprotocol/extension_test.go
|
||||||
|
@@ -29,7 +29,8 @@ func TestCompactIP(t *testing.T) {
|
||||||
|
var ip CompactIP
|
||||||
|
if err = ip.UnmarshalBencode(b); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
- } else if ip.String() != "1.2.3.4" {
|
||||||
|
+ } else if ip.String() != "123.2.3.4" {
|
||||||
|
+ t.Log("IP", ip.String(), "IP")
|
||||||
|
t.Error(ip)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -26,8 +26,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
pp "github.com/xgfone/bt/peerprotocol"
|
pp "github.com/eyedeekay/go-i2p-bt/peerprotocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlockSize is the size of a block of the piece.
|
// BlockSize is the size of a block of the piece.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,11 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package downloader
|
package blockdownload
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
pp "github.com/xgfone/bt/peerprotocol"
|
pp "github.com/eyedeekay/go-i2p-bt/peerprotocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlockDownloadHandler is used to downloads the files in the torrent file.
|
// BlockDownloadHandler is used to downloads the files in the torrent file.
|
5
go.mod
5
go.mod
@ -1,3 +1,8 @@
|
|||||||
module github.com/xgfone/bt
|
module github.com/xgfone/bt
|
||||||
|
|
||||||
go 1.11
|
go 1.11
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/eyedeekay/i2pkeys v0.33.0
|
||||||
|
github.com/eyedeekay/sam3 v0.33.5
|
||||||
|
)
|
||||||
|
62
go.sum
Normal file
62
go.sum
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
|
||||||
|
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
|
||||||
|
github.com/eyedeekay/i2pkeys v0.33.0 h1:5SzUyWxNjV6AvYv/WaI8J4dSgAfv7/WEps6pDLe2YSs=
|
||||||
|
github.com/eyedeekay/i2pkeys v0.33.0/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
|
||||||
|
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
||||||
|
github.com/eyedeekay/sam3 v0.33.5 h1:mY2MmEG4W35AOpG/G7DOdAhFZWRwFxlm+NmIoub1Xnw=
|
||||||
|
github.com/eyedeekay/sam3 v0.33.5/go.mod h1:sPtlI4cRm7wD0UywOzLPvvdY1G++vBSK3n+jiIGqWlU=
|
||||||
|
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||||
|
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||||
|
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
|
||||||
|
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
|
||||||
|
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
|
||||||
|
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||||
|
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
|
||||||
|
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||||
|
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/renameio v1.0.0/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33/go.mod h1:BjmVxzAnkLeoEbqHEerI4eSw6ua+RaIB0S4jMV21RAs=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,8 +18,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Predefine some error code.
|
// Predefine some error code.
|
||||||
@ -337,7 +337,7 @@ func (cn CompactIPv4Node) MarshalBinary() ([]byte, error) {
|
|||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
buf.Grow(26 * len(cn))
|
buf.Grow(26 * len(cn))
|
||||||
for _, ni := range cn {
|
for _, ni := range cn {
|
||||||
if ni.Addr.IP = ni.Addr.IP.To4(); len(ni.Addr.IP) == 0 {
|
if ni.Addr.IP = ni.Addr.To4(); len(ni.Addr.IP.String()) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if n, err := ni.WriteBinary(buf); err != nil {
|
if n, err := ni.WriteBinary(buf); err != nil {
|
||||||
@ -396,7 +396,7 @@ func (cn CompactIPv6Node) MarshalBinary() ([]byte, error) {
|
|||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
buf.Grow(38 * len(cn))
|
buf.Grow(38 * len(cn))
|
||||||
for _, ni := range cn {
|
for _, ni := range cn {
|
||||||
ni.Addr.IP = ni.Addr.IP.To16()
|
ni.Addr.IP = ni.Addr.To16()
|
||||||
if n, err := ni.WriteBinary(buf); err != nil {
|
if n, err := ni.WriteBinary(buf); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if n != 38 {
|
} else if n != 38 {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -17,7 +17,7 @@ package krpc
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMessage(t *testing.T) {
|
func TestMessage(t *testing.T) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -20,7 +20,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node represents a node information.
|
// Node represents a node information.
|
||||||
@ -35,7 +35,7 @@ func NewNode(id metainfo.Hash, ip net.IP, port int) Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewNodeByUDPAddr returns a new Node with the id and the UDP address.
|
// NewNodeByUDPAddr returns a new Node with the id and the UDP address.
|
||||||
func NewNodeByUDPAddr(id metainfo.Hash, addr *net.UDPAddr) (n Node) {
|
func NewNodeByUDPAddr(id metainfo.Hash, addr net.Addr) (n Node) {
|
||||||
n.ID = id
|
n.ID = id
|
||||||
n.Addr.FromUDPAddr(addr)
|
n.Addr.FromUDPAddr(addr)
|
||||||
return
|
return
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -21,8 +21,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/i2pkeys"
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrInvalidAddr is returned when the compact address is invalid.
|
// ErrInvalidAddr is returned when the compact address is invalid.
|
||||||
@ -31,16 +34,24 @@ var ErrInvalidAddr = fmt.Errorf("invalid compact information of ip and port")
|
|||||||
// Address represents a client/server listening on a UDP port implementing
|
// Address represents a client/server listening on a UDP port implementing
|
||||||
// the DHT protocol.
|
// the DHT protocol.
|
||||||
type Address struct {
|
type Address struct {
|
||||||
IP net.IP // For IPv4, its length must be 4.
|
IP net.Addr
|
||||||
Port uint16
|
Port uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAddress returns a new Address.
|
// NewAddress returns a new Address.
|
||||||
func NewAddress(ip net.IP, port uint16) Address {
|
func NewAddress(ip interface{}, port uint16) Address {
|
||||||
if ipv4 := ip.To4(); len(ipv4) > 0 {
|
switch v := ip.(type) {
|
||||||
ip = ipv4
|
case net.IP:
|
||||||
|
return Address{IP: &net.IPAddr{
|
||||||
|
IP: v,
|
||||||
|
}, Port: port}
|
||||||
|
case *net.IPAddr:
|
||||||
|
return Address{IP: v, Port: port}
|
||||||
|
case i2pkeys.I2PAddr:
|
||||||
|
return Address{IP: v, Port: port}
|
||||||
|
default:
|
||||||
|
return Address{IP: nil, Port: 0}
|
||||||
}
|
}
|
||||||
return Address{IP: ip, Port: port}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAddressFromString returns a new Address by the address string.
|
// NewAddressFromString returns a new Address by the address string.
|
||||||
@ -49,11 +60,53 @@ func NewAddressFromString(s string) (addr Address, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Lookup(shost string, port int) ([]Address, error) {
|
||||||
|
if strings.HasSuffix(shost, ".i2p") {
|
||||||
|
iaddr, err := i2pkeys.Lookup(shost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
returnAddresses := []Address{
|
||||||
|
{IP: iaddr, Port: 6881},
|
||||||
|
}
|
||||||
|
return returnAddresses, nil
|
||||||
|
}
|
||||||
|
ips, err := net.LookupIP(shost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("fail to lookup the domain '%s': %s", shost, err)
|
||||||
|
}
|
||||||
|
returnAddrs := make([]Address, len(ips))
|
||||||
|
for i, ip := range ips {
|
||||||
|
if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
returnAddrs[i] = Address{
|
||||||
|
IP: &net.IPAddr{
|
||||||
|
IP: ipv4,
|
||||||
|
},
|
||||||
|
Port: uint16(port),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
returnAddrs[i] = Address{
|
||||||
|
IP: &net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
},
|
||||||
|
Port: uint16(port),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewAddressesFromString returns a list of Addresses by the address string.
|
// NewAddressesFromString returns a list of Addresses by the address string.
|
||||||
func NewAddressesFromString(s string) (addrs []Address, err error) {
|
func NewAddressesFromString(s string) (addrs []Address, err error) {
|
||||||
shost, sport, err := net.SplitHostPort(s)
|
shost, sport, err := net.SplitHostPort(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid address '%s': %s", s, err)
|
if sport == "" && strings.HasSuffix(shost, ".i2p") {
|
||||||
|
//Skip the missing port error if the address is an i2p address,
|
||||||
|
//since the port is optional and we can assign 6881 automatically.
|
||||||
|
sport = "6881"
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid address '%s': %s", s, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var port uint16
|
var port uint16
|
||||||
@ -65,28 +118,54 @@ func NewAddressesFromString(s string) (addrs []Address, err error) {
|
|||||||
port = uint16(v)
|
port = uint16(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addrs, err = Lookup(shost, int(port))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("fail to lookup the domain '%s': %s", shost, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func LookupNetAddr(shost string) ([]net.Addr, error) {
|
||||||
|
if strings.HasSuffix(shost, ".i2p") {
|
||||||
|
iaddr, err := i2pkeys.Lookup(shost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
returnAddresses := []net.Addr{
|
||||||
|
iaddr,
|
||||||
|
}
|
||||||
|
return returnAddresses, nil
|
||||||
|
}
|
||||||
ips, err := net.LookupIP(shost)
|
ips, err := net.LookupIP(shost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fail to lookup the domain '%s': %s", shost, err)
|
return nil, fmt.Errorf("fail to lookup the domain '%s': %s", shost, err)
|
||||||
}
|
}
|
||||||
|
returnAddrs := make([]net.Addr, len(ips))
|
||||||
addrs = make([]Address, len(ips))
|
|
||||||
for i, ip := range ips {
|
for i, ip := range ips {
|
||||||
if ipv4 := ip.To4(); len(ipv4) != 0 {
|
if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
addrs[i] = Address{IP: ipv4, Port: port}
|
returnAddrs[i] = &net.IPAddr{
|
||||||
|
IP: ipv4,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
addrs[i] = Address{IP: ip, Port: port}
|
returnAddrs[i] = &net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return returnAddrs, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromString parses and sets the ip from the string addr.
|
// FromString parses and sets the ip from the string addr.
|
||||||
func (a *Address) FromString(addr string) (err error) {
|
func (a *Address) FromString(addr string) (err error) {
|
||||||
host, port, err := net.SplitHostPort(addr)
|
host, port, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid address '%s': %s", addr, err)
|
if port == "" && strings.HasSuffix(host, ".i2p") {
|
||||||
|
//Skip the missing port error if the address is an i2p address,
|
||||||
|
//since the port is optional and we can assign 6881 automatically.
|
||||||
|
port = "6881"
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid address '%s': %s", addr, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if port != "" {
|
if port != "" {
|
||||||
@ -97,7 +176,7 @@ func (a *Address) FromString(addr string) (err error) {
|
|||||||
a.Port = uint16(v)
|
a.Port = uint16(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, err := net.LookupIP(host)
|
ips, err := LookupNetAddr(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("fail to lookup the domain '%s': %s", host, err)
|
return fmt.Errorf("fail to lookup the domain '%s': %s", host, err)
|
||||||
} else if len(ips) == 0 {
|
} else if len(ips) == 0 {
|
||||||
@ -105,27 +184,81 @@ func (a *Address) FromString(addr string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.IP = ips[0]
|
a.IP = ips[0]
|
||||||
if ip := a.IP.To4(); len(ip) > 0 {
|
|
||||||
a.IP = ip
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromUDPAddr sets the ip from net.UDPAddr.
|
// FromUDPAddr sets the ip from net.UDPAddr.
|
||||||
func (a *Address) FromUDPAddr(ua *net.UDPAddr) {
|
func (a *Address) FromUDPAddr(ua net.Addr) {
|
||||||
a.Port = uint16(ua.Port)
|
a.Port = uint16(utils.Port(ua))
|
||||||
a.IP = ua.IP
|
a.IP = ua
|
||||||
if ipv4 := a.IP.To4(); len(ipv4) != 0 {
|
}
|
||||||
a.IP = ipv4
|
|
||||||
|
// Addr creates a new net.Addr.
|
||||||
|
func (a Address) Addr() net.Addr {
|
||||||
|
switch a.IP.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
return &net.UDPAddr{
|
||||||
|
IP: a.IP.(*net.IPAddr).IP,
|
||||||
|
Port: int(a.Port),
|
||||||
|
}
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return &net.UDPAddr{
|
||||||
|
IP: a.IP.(*net.UDPAddr).IP,
|
||||||
|
Port: int(a.Port),
|
||||||
|
}
|
||||||
|
case *i2pkeys.I2PAddr:
|
||||||
|
retvalue := a.IP.(*i2pkeys.I2PAddr)
|
||||||
|
return retvalue
|
||||||
|
case i2pkeys.I2PAddr:
|
||||||
|
retvalue := a.IP.(i2pkeys.I2PAddr)
|
||||||
|
return &retvalue
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UDPAddr creates a new net.UDPAddr.
|
func (a Address) IsIPv6() bool {
|
||||||
func (a Address) UDPAddr() *net.UDPAddr {
|
switch a.IP.(type) {
|
||||||
return &net.UDPAddr{
|
case *net.IPAddr:
|
||||||
IP: a.IP,
|
return a.IP.(*net.IPAddr).IP.To4() == nil
|
||||||
Port: int(a.Port),
|
case *net.UDPAddr:
|
||||||
|
return a.IP.(*net.UDPAddr).IP.To4() == nil
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Address) To4() *net.IPAddr {
|
||||||
|
switch a.IP.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
return &net.IPAddr{
|
||||||
|
IP: a.IP.(*net.IPAddr).IP.To4(),
|
||||||
|
}
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return &net.IPAddr{
|
||||||
|
IP: a.IP.(*net.UDPAddr).IP.To4(),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return &net.IPAddr{
|
||||||
|
IP: net.IP{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Address) To16() *net.IPAddr {
|
||||||
|
switch a.IP.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
return &net.IPAddr{
|
||||||
|
IP: a.IP.(*net.IPAddr).IP.To16(),
|
||||||
|
}
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return &net.IPAddr{
|
||||||
|
IP: a.IP.(*net.UDPAddr).IP.To16(),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return &net.IPAddr{
|
||||||
|
IP: net.IPv6loopback,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,20 +270,35 @@ func (a Address) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Equal reports whether n is equal to o, which is equal to
|
// Equal reports whether n is equal to o, which is equal to
|
||||||
// n.HasIPAndPort(o.IP, o.Port)
|
//
|
||||||
|
// n.HasIPAndPort(o.IP, o.Port)
|
||||||
func (a Address) Equal(o Address) bool {
|
func (a Address) Equal(o Address) bool {
|
||||||
return a.Port == o.Port && a.IP.Equal(o.IP)
|
return a.Port == o.Port && a.IP.String() == o.IP.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasIPAndPort reports whether the current node has the ip and the port.
|
// HasIPAndPort reports whether the current node has the ip and the port.
|
||||||
func (a Address) HasIPAndPort(ip net.IP, port uint16) bool {
|
func (a Address) HasIPAndPort(ip net.Addr, port uint16) bool {
|
||||||
return port == a.Port && a.IP.Equal(ip)
|
return port == a.Port && a.IP.String() == ip.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBinary is the same as MarshalBinary, but writes the result into w
|
// WriteBinary is the same as MarshalBinary, but writes the result into w
|
||||||
// instead of returning.
|
// instead of returning.
|
||||||
func (a Address) WriteBinary(w io.Writer) (m int, err error) {
|
func (a Address) WriteBinary(w io.Writer) (m int, err error) {
|
||||||
if m, err = w.Write(a.IP); err == nil {
|
var ip []byte
|
||||||
|
switch a.IP.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = []byte(a.IP.(*net.IPAddr).IP)
|
||||||
|
case *net.UDPAddr:
|
||||||
|
ip = []byte(a.IP.(*net.UDPAddr).IP)
|
||||||
|
case i2pkeys.I2PAddr:
|
||||||
|
var err error
|
||||||
|
ip, err = a.IP.(i2pkeys.I2PAddr).ToBytes()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, err = w.Write(ip); err == nil {
|
||||||
if err = binary.Write(w, binary.BigEndian, a.Port); err == nil {
|
if err = binary.Write(w, binary.BigEndian, a.Port); err == nil {
|
||||||
m += 2
|
m += 2
|
||||||
}
|
}
|
||||||
@ -161,15 +309,33 @@ func (a Address) WriteBinary(w io.Writer) (m int, err error) {
|
|||||||
// UnmarshalBinary implements the interface binary.BinaryUnmarshaler.
|
// UnmarshalBinary implements the interface binary.BinaryUnmarshaler.
|
||||||
func (a *Address) UnmarshalBinary(b []byte) (err error) {
|
func (a *Address) UnmarshalBinary(b []byte) (err error) {
|
||||||
_len := len(b) - 2
|
_len := len(b) - 2
|
||||||
switch _len {
|
i2p := false
|
||||||
case net.IPv4len, net.IPv6len:
|
if _len <= net.IPv6len {
|
||||||
default:
|
switch _len {
|
||||||
return ErrInvalidAddr
|
case net.IPv4len, net.IPv6len:
|
||||||
|
default:
|
||||||
|
return ErrInvalidAddr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_len = len(b)
|
||||||
|
i2p = true
|
||||||
|
}
|
||||||
|
|
||||||
|
IP := make([]byte, _len)
|
||||||
|
copy(IP, b[:_len])
|
||||||
|
if i2p {
|
||||||
|
a.IP, err = i2pkeys.NewI2PAddrFromBytes(IP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.Port = uint16(6881)
|
||||||
|
} else {
|
||||||
|
a.IP = &net.IPAddr{
|
||||||
|
IP: net.IP(IP),
|
||||||
|
}
|
||||||
|
a.Port = binary.BigEndian.Uint16(b[_len:])
|
||||||
}
|
}
|
||||||
|
|
||||||
a.IP = make(net.IP, _len)
|
|
||||||
copy(a.IP, b[:_len])
|
|
||||||
a.Port = binary.BigEndian.Uint16(b[_len:])
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,14 +361,27 @@ func (a *Address) decode(vs []interface{}) (err error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
host := vs[0].(string)
|
host := vs[0].(string)
|
||||||
if a.IP = net.ParseIP(host); len(a.IP) == 0 {
|
if strings.HasSuffix(host, ".i2p") {
|
||||||
return ErrInvalidAddr
|
a.IP, err = i2pkeys.NewI2PAddrFromBytes([]byte(host))
|
||||||
} else if ip := a.IP.To4(); len(ip) != 0 {
|
if err != nil {
|
||||||
a.IP = ip
|
return
|
||||||
|
}
|
||||||
|
a.Port = uint16(6881)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
a.IP = &net.IPAddr{
|
||||||
|
IP: net.ParseIP(host),
|
||||||
|
}
|
||||||
|
if len(a.IP.(*net.IPAddr).IP) == 0 {
|
||||||
|
return ErrInvalidAddr
|
||||||
|
} else if ip := a.IP.(*net.IPAddr).IP.To4(); len(ip) != 0 {
|
||||||
|
a.IP = &net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.Port = uint16(vs[1].(int64))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Port = uint16(vs[1].(int64))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalBencode implements the interface bencode.Unmarshaler.
|
// UnmarshalBencode implements the interface bencode.Unmarshaler.
|
||||||
@ -352,3 +531,19 @@ func (a HostAddress) MarshalBencode() (b []byte, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConvertAddress(addr net.Addr) (a Address, err error) {
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
a = NewAddress(v, uint16(v.Port))
|
||||||
|
case *net.UDPAddr:
|
||||||
|
a = NewAddress(v, uint16(v.Port))
|
||||||
|
case *i2pkeys.I2PAddr:
|
||||||
|
a = NewAddress(v, 6881)
|
||||||
|
case i2pkeys.I2PAddr:
|
||||||
|
a = NewAddress(v, 6881)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported address type: %T", addr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -24,7 +24,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var zeroHash Hash
|
var zeroHash Hash
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -20,8 +20,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
"github.com/xgfone/bt/utils"
|
"github.com/eyedeekay/go-i2p-bt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bytes is the []byte type.
|
// Bytes is the []byte type.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -21,7 +21,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/xgfone/bt/utils"
|
"github.com/eyedeekay/go-i2p-bt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Predefine some sizes of the pieces.
|
// Predefine some sizes of the pieces.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -19,7 +19,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/i2pkeys"
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInvalidIP = errors.New("invalid ipv4 or ipv6")
|
var errInvalidIP = errors.New("invalid ipv4 or ipv6")
|
||||||
@ -43,40 +44,74 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CompactIP is used to handle the compact ipv4 or ipv6.
|
// CompactIP is used to handle the compact ipv4 or ipv6.
|
||||||
type CompactIP net.IP
|
type CompactIP []byte
|
||||||
|
|
||||||
func (ci CompactIP) String() string {
|
func (ci CompactIP) String() string {
|
||||||
return net.IP(ci).String()
|
//return net.IP(ci).String()
|
||||||
|
switch len(ci) {
|
||||||
|
case net.IPv4len:
|
||||||
|
return net.IP(ci).String()
|
||||||
|
case net.IPv6len:
|
||||||
|
return net.IP(ci).String()
|
||||||
|
case 32:
|
||||||
|
i2p, _ := i2pkeys.DestHashFromBytes(ci)
|
||||||
|
return i2p.String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalBencode implements the interface bencode.Marshaler.
|
// MarshalBencode implements the interface bencode.Marshaler.
|
||||||
func (ci CompactIP) MarshalBencode() ([]byte, error) {
|
func (ci CompactIP) MarshalBencode() ([]byte, error) {
|
||||||
ip := net.IP(ci)
|
if len(ci) == net.IPv4len {
|
||||||
if ipv4 := ip.To4(); len(ipv4) != 0 {
|
ip := []byte(ci)
|
||||||
ip = ipv4
|
ic, err := bencode.EncodeBytes(ip[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ic, nil
|
||||||
}
|
}
|
||||||
return bencode.EncodeBytes(ip[:])
|
if len(ci) == net.IPv6len {
|
||||||
|
ip := []byte(ci)
|
||||||
|
ic, err := bencode.EncodeBytes(ip[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ic, nil
|
||||||
|
}
|
||||||
|
if len(ci) == 32 {
|
||||||
|
i2p, err := i2pkeys.DestHashFromBytes(ci[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bencode.EncodeBytes(i2p[:])
|
||||||
|
}
|
||||||
|
return nil, errInvalidIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalBencode implements the interface bencode.Unmarshaler.
|
// UnmarshalBencode implements the interface bencode.Unmarshaler.
|
||||||
func (ci *CompactIP) UnmarshalBencode(b []byte) (err error) {
|
func (ci *CompactIP) UnmarshalBencode(b []byte) (err error) {
|
||||||
var ip net.IP
|
if len(b) >= net.IPv4len && len(b) < net.IPv6len {
|
||||||
if err = bencode.DecodeBytes(b, &ip); err != nil {
|
ip := net.IP(b[len(b)-net.IPv4len:])
|
||||||
return
|
if ipv4 := ip.To4(); len(ipv4) != 0 {
|
||||||
|
ip = ipv4
|
||||||
|
}
|
||||||
|
*ci = CompactIP(ip[:])
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
if len(b) >= net.IPv6len && len(b) < 32 {
|
||||||
switch len(ip) {
|
ip := net.IP(b[len(b)-net.IPv6len:])
|
||||||
case net.IPv4len, net.IPv6len:
|
if ipv6 := ip.To16(); len(ipv6) != 0 {
|
||||||
default:
|
ip = ipv6
|
||||||
return errInvalidIP
|
}
|
||||||
|
*ci = CompactIP(ip[:])
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
if len(b) >= 32 {
|
||||||
if ipv4 := ip.To4(); len(ipv4) != 0 {
|
i2p, _ := i2pkeys.DestHashFromBytes(b[len(b)-32:])
|
||||||
ip = ipv4
|
*ci = i2p[:]
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
return errInvalidIP
|
||||||
*ci = CompactIP(ip)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtendedHandshakeMsg represent the extended handshake message.
|
// ExtendedHandshakeMsg represent the extended handshake message.
|
||||||
@ -91,7 +126,7 @@ type ExtendedHandshakeMsg struct {
|
|||||||
// Port is the local client port, which is redundant and no need
|
// Port is the local client port, which is redundant and no need
|
||||||
// for the receiving side of the connection to send this.
|
// for the receiving side of the connection to send this.
|
||||||
Port uint16 `bencode:"p,omitempty"` // BEP 10
|
Port uint16 `bencode:"p,omitempty"` // BEP 10
|
||||||
IPv6 net.IP `bencode:"ipv6,omitempty"` // BEP 10
|
IPv6 []byte `bencode:"ipv6,omitempty"` // BEP 10
|
||||||
IPv4 CompactIP `bencode:"ipv4,omitempty"` // BEP 10
|
IPv4 CompactIP `bencode:"ipv4,omitempty"` // BEP 10
|
||||||
YourIP CompactIP `bencode:"yourip,omitempty"` // BEP 10
|
YourIP CompactIP `bencode:"yourip,omitempty"` // BEP 10
|
||||||
|
|
||||||
@ -139,10 +174,9 @@ func (um UtMetadataExtendedMsg) EncodeToPayload(buf *bytes.Buffer) (err error) {
|
|||||||
|
|
||||||
// EncodeToBytes is equal to
|
// EncodeToBytes is equal to
|
||||||
//
|
//
|
||||||
// buf := new(bytes.Buffer)
|
// buf := new(bytes.Buffer)
|
||||||
// err = um.EncodeToPayload(buf)
|
// err = um.EncodeToPayload(buf)
|
||||||
// return buf.Bytes(), err
|
// return buf.Bytes(), err
|
||||||
//
|
|
||||||
func (um UtMetadataExtendedMsg) EncodeToBytes() (b []byte, err error) {
|
func (um UtMetadataExtendedMsg) EncodeToBytes() (b []byte, err error) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 128))
|
buf := bytes.NewBuffer(make([]byte, 0, 128))
|
||||||
if err = um.EncodeToPayload(buf); err == nil {
|
if err = um.EncodeToPayload(buf); err == nil {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +16,10 @@ package peerprotocol
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/eyedeekay/sam3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompactIP(t *testing.T) {
|
func TestCompactIP(t *testing.T) {
|
||||||
@ -26,11 +29,57 @@ func TestCompactIP(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("IPv4 Test", ipv4.String(), len(ipv4))
|
||||||
|
|
||||||
|
var ip CompactIP
|
||||||
|
if err = ip.UnmarshalBencode(b); err != nil {
|
||||||
|
t.Error(err, ip)
|
||||||
|
} else if ip.String() != "1.2.3.4" {
|
||||||
|
t.Error(ip.String(), ",", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompactIP6(t *testing.T) {
|
||||||
|
ipv6 := CompactIP([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})
|
||||||
|
b, err := ipv6.MarshalBencode()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("IPv6 Test", ipv6.String(), len(ipv6))
|
||||||
|
|
||||||
var ip CompactIP
|
var ip CompactIP
|
||||||
if err = ip.UnmarshalBencode(b); err != nil {
|
if err = ip.UnmarshalBencode(b); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else if ip.String() != "1.2.3.4" {
|
} else if ip.String() != "102:304:506:708:90a:b0c:d0e:f10" {
|
||||||
t.Error(ip)
|
t.Error(ip.String(), ",", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompactI2P(t *testing.T) {
|
||||||
|
sam, err := sam3.NewSAM("127.0.0.1:7656")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer sam.Close()
|
||||||
|
i2pkeys, err := sam.NewKeys()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dh := i2pkeys.Address.DestHash()
|
||||||
|
i2p := CompactIP(dh[:])
|
||||||
|
b, err := i2p.MarshalBencode()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("I2P Test", i2p.String(), len(b))
|
||||||
|
|
||||||
|
var ip CompactIP
|
||||||
|
if err = ip.UnmarshalBencode(b); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if ip.String() != dh.String() {
|
||||||
|
t.Error(ip, dh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -19,16 +19,17 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateAllowedFastSet generates some allowed fast set of the torrent file.
|
// GenerateAllowedFastSet generates some allowed fast set of the torrent file.
|
||||||
//
|
//
|
||||||
// Argument:
|
// Argument:
|
||||||
// set: generated piece set, the length of which is the number to be generated.
|
//
|
||||||
// sz: the number of pieces in torrent.
|
// set: generated piece set, the length of which is the number to be generated.
|
||||||
// ip: the of the remote peer of the connection.
|
// sz: the number of pieces in torrent.
|
||||||
// infohash: infohash of torrent.
|
// ip: the of the remote peer of the connection.
|
||||||
|
// infohash: infohash of torrent.
|
||||||
//
|
//
|
||||||
// BEP 6
|
// BEP 6
|
||||||
func GenerateAllowedFastSet(set []uint32, sz uint32, ip net.IP, infohash metainfo.Hash) {
|
func GenerateAllowedFastSet(set []uint32, sz uint32, ip net.IP, infohash metainfo.Hash) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,7 +18,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateAllowedFastSet(t *testing.T) {
|
func TestGenerateAllowedFastSet(t *testing.T) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -19,7 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInvalidProtocolHeader = fmt.Errorf("unexpected peer protocol header string")
|
var errInvalidProtocolHeader = fmt.Errorf("unexpected peer protocol header string")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -21,8 +21,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Predefine some errors about extension support.
|
// Predefine some errors about extension support.
|
||||||
@ -36,6 +36,9 @@ var (
|
|||||||
ErrNoExtHandshake = fmt.Errorf("no extended handshake")
|
ErrNoExtHandshake = fmt.Errorf("no extended handshake")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var DialTimeout = net.DialTimeout
|
||||||
|
var Dial = net.Dial
|
||||||
|
|
||||||
// Bep3Handler is used to handle the BEP 3 type message if Handler has also
|
// Bep3Handler is used to handle the BEP 3 type message if Handler has also
|
||||||
// implemented the interface.
|
// implemented the interface.
|
||||||
type Bep3Handler interface {
|
type Bep3Handler interface {
|
||||||
@ -169,7 +172,7 @@ func NewPeerConn(conn net.Conn, id, infohash metainfo.Hash) *PeerConn {
|
|||||||
|
|
||||||
// NewPeerConnByDial returns a new PeerConn by dialing to addr with the "tcp" network.
|
// NewPeerConnByDial returns a new PeerConn by dialing to addr with the "tcp" network.
|
||||||
func NewPeerConnByDial(addr string, id, infohash metainfo.Hash, timeout time.Duration) (pc *PeerConn, err error) {
|
func NewPeerConnByDial(addr string, id, infohash metainfo.Hash, timeout time.Duration) (pc *PeerConn, err error) {
|
||||||
conn, err := net.DialTimeout("tcp", addr, timeout)
|
conn, err := DialTimeout("tcp", addr, timeout)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
pc = NewPeerConn(conn, id, infohash)
|
pc = NewPeerConn(conn, id, infohash)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -22,7 +22,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler is used to handle the incoming peer connection.
|
// Handler is used to handle the incoming peer connection.
|
||||||
@ -65,6 +65,8 @@ type Config struct {
|
|||||||
HandleMessage func(pc *PeerConn, msg Message, handler Handler) error
|
HandleMessage func(pc *PeerConn, msg Message, handler Handler) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Listen = net.Listen
|
||||||
|
|
||||||
func (c *Config) set(conf ...Config) {
|
func (c *Config) set(conf ...Config) {
|
||||||
if len(conf) > 0 {
|
if len(conf) > 0 {
|
||||||
*c = conf[0]
|
*c = conf[0]
|
||||||
@ -95,7 +97,7 @@ type Server struct {
|
|||||||
// NewServerByListen returns a new Server by listening on the address.
|
// NewServerByListen returns a new Server by listening on the address.
|
||||||
func NewServerByListen(network, address string, id metainfo.Hash, h Handler,
|
func NewServerByListen(network, address string, id metainfo.Hash, h Handler,
|
||||||
c ...Config) (*Server, error) {
|
c ...Config) (*Server, error) {
|
||||||
ln, err := net.Listen(network, address)
|
ln, err := Listen(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -16,12 +16,16 @@ package tracker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Force a peer address to be used
|
||||||
|
var PeerAddress net.Addr
|
||||||
|
|
||||||
// GetPeersResult represents the result of getting the peers from the tracker.
|
// GetPeersResult represents the result of getting the peers from the tracker.
|
||||||
type GetPeersResult struct {
|
type GetPeersResult struct {
|
||||||
Error error // nil stands for success. Or, for failure.
|
Error error // nil stands for success. Or, for failure.
|
||||||
@ -89,8 +93,7 @@ func getPeers(ctx context.Context, wg *sync.WaitGroup, tracker string,
|
|||||||
|
|
||||||
client, err := NewClient(tracker, ClientConfig{ID: nodeID})
|
client, err := NewClient(tracker, ClientConfig{ID: nodeID})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
resp, err = client.Announce(ctx, AnnounceRequest{InfoHash: infoHash})
|
resp, err = client.Announce(ctx, AnnounceRequest{InfoHash: infoHash, IP: PeerAddress})
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -21,6 +21,9 @@ package httptracker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -28,8 +31,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AnnounceRequest is the tracker announce requests.
|
// AnnounceRequest is the tracker announce requests.
|
||||||
@ -290,6 +293,28 @@ func NewClient(announceURL, scrapeURL string) *Client {
|
|||||||
func (t *Client) Close() error { return nil }
|
func (t *Client) Close() error { return nil }
|
||||||
func (t *Client) String() string { return t.AnnounceURL }
|
func (t *Client) String() string { return t.AnnounceURL }
|
||||||
|
|
||||||
|
var (
|
||||||
|
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
|
||||||
|
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Base32(b64addr string) string {
|
||||||
|
if len(b64addr) > 300 {
|
||||||
|
b64addr = strings.TrimSuffix(b64addr, ".i2p")
|
||||||
|
b64, err := i2pB64enc.DecodeString(b64addr)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
//hash.Write([]byte(b64))
|
||||||
|
var s []byte
|
||||||
|
for _, e := range sha256.Sum256(b64) {
|
||||||
|
s = append(s, e)
|
||||||
|
}
|
||||||
|
return strings.ToLower(strings.Replace(i2pB32enc.EncodeToString(s), "=", "", -1)) + ".b32.i2p"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Client) send(c context.Context, u string, vs url.Values, r interface{}) (err error) {
|
func (t *Client) send(c context.Context, u string, vs url.Values, r interface{}) (err error) {
|
||||||
var url string
|
var url string
|
||||||
if strings.IndexByte(u, '?') < 0 {
|
if strings.IndexByte(u, '?') < 0 {
|
||||||
@ -310,6 +335,10 @@ func (t *Client) send(c context.Context, u string, vs url.Values, r interface{})
|
|||||||
resp, err = t.Client.Do(req)
|
resp, err = t.Client.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if resp.Body != nil {
|
if resp.Body != nil {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
}
|
}
|
||||||
@ -322,7 +351,7 @@ func (t *Client) send(c context.Context, u string, vs url.Values, r interface{})
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Announce sends a Announce request to the tracker.
|
// Announce sends a Announce request to the tracker.
|
||||||
func (t *Client) Announce(c context.Context, req AnnounceRequest) (resp AnnounceResponse, err error) {
|
func (t *Client) Announce(c context.Context, req *AnnounceRequest) (resp AnnounceResponse, err error) {
|
||||||
if req.PeerID.IsZero() {
|
if req.PeerID.IsZero() {
|
||||||
if t.ID.IsZero() {
|
if t.ID.IsZero() {
|
||||||
req.PeerID = metainfo.NewRandomHash()
|
req.PeerID = metainfo.NewRandomHash()
|
||||||
@ -332,6 +361,7 @@ func (t *Client) Announce(c context.Context, req AnnounceRequest) (resp Announce
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = t.send(c, t.AnnounceURL, req.ToQuery(), &resp)
|
err = t.send(c, t.AnnounceURL, req.ToQuery(), &resp)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -19,9 +19,11 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/xgfone/bt/bencode"
|
"github.com/eyedeekay/i2pkeys"
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/bencode"
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInvalidPeer = errors.New("invalid peer information format")
|
var errInvalidPeer = errors.New("invalid peer information format")
|
||||||
@ -38,15 +40,24 @@ type Peer struct {
|
|||||||
|
|
||||||
// Addresses returns the list of the addresses that the peer listens on.
|
// Addresses returns the list of the addresses that the peer listens on.
|
||||||
func (p Peer) Addresses() (addrs []metainfo.Address, err error) {
|
func (p Peer) Addresses() (addrs []metainfo.Address, err error) {
|
||||||
|
if strings.HasSuffix(p.IP, ".i2p") {
|
||||||
|
var netAddr net.Addr
|
||||||
|
if netAddr, err = i2pkeys.NewI2PAddrFromString(strings.TrimSuffix(p.IP, ".i2p")); err != nil {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
return []metainfo.Address{{IP: netAddr, Port: p.Port}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
if ip := net.ParseIP(p.IP); len(ip) != 0 {
|
if ip := net.ParseIP(p.IP); len(ip) != 0 {
|
||||||
return []metainfo.Address{{IP: ip, Port: p.Port}}, nil
|
return []metainfo.Address{{IP: &net.IPAddr{IP: ip}, Port: p.Port}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, err := net.LookupIP(p.IP)
|
ips, err := net.LookupIP(p.IP)
|
||||||
if _len := len(ips); err == nil && len(ips) != 0 {
|
if _len := len(ips); err == nil && len(ips) != 0 {
|
||||||
addrs = make([]metainfo.Address, _len)
|
addrs = make([]metainfo.Address, _len)
|
||||||
for i, ip := range ips {
|
for i, ip := range ips {
|
||||||
addrs[i] = metainfo.Address{IP: ip, Port: p.Port}
|
addrs[i] = metainfo.Address{IP: &net.IPAddr{IP: ip}, Port: p.Port}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,16 +84,19 @@ func (ps *Peers) UnmarshalBencode(b []byte) (err error) {
|
|||||||
peers := make(Peers, 0, _len/6)
|
peers := make(Peers, 0, _len/6)
|
||||||
for i := 0; i < _len; i += 6 {
|
for i := 0; i < _len; i += 6 {
|
||||||
var addr metainfo.Address
|
var addr metainfo.Address
|
||||||
if err = addr.UnmarshalBinary([]byte(vs[i : i+6])); err != nil {
|
addrBytes := []byte(vs[i : i+6])
|
||||||
|
if err = addr.UnmarshalBinary(addrBytes); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
peers = append(peers, Peer{IP: addr.IP.String(), Port: addr.Port})
|
peers = append(peers, Peer{IP: addr.IP.String(), Port: addr.Port})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*ps = peers
|
*ps = peers
|
||||||
case []interface{}: // BEP 3
|
case []interface{}: // BEP 3
|
||||||
peers := make(Peers, len(vs))
|
peers := make(Peers, len(vs))
|
||||||
for i, p := range vs {
|
for i, p := range vs {
|
||||||
|
|
||||||
m, ok := p.(map[string]interface{})
|
m, ok := p.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return errInvalidPeer
|
return errInvalidPeer
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,7 +18,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHTTPAnnounceRequest(t *testing.T) {
|
func TestHTTPAnnounceRequest(t *testing.T) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -26,9 +26,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
"github.com/xgfone/bt/tracker/httptracker"
|
"github.com/eyedeekay/go-i2p-bt/tracker/httptracker"
|
||||||
"github.com/xgfone/bt/tracker/udptracker"
|
"github.com/eyedeekay/go-i2p-bt/tracker/udptracker"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Predefine some announce events.
|
// Predefine some announce events.
|
||||||
@ -53,20 +53,24 @@ type AnnounceRequest struct {
|
|||||||
Left int64 // Required, but default: 0, which should be only used for test or last.
|
Left int64 // Required, but default: 0, which should be only used for test or last.
|
||||||
Event uint32 // Required, but default: 0
|
Event uint32 // Required, but default: 0
|
||||||
|
|
||||||
IP net.IP // Optional
|
IP net.Addr // Optional
|
||||||
Key int32 // Optional
|
Key int32 // Optional
|
||||||
NumWant int32 // Optional, BEP 15: -1 for default. But we use 0 as default.
|
NumWant int32 // Optional, BEP 15: -1 for default. But we use 0 as default.
|
||||||
Port uint16 // Optional
|
Port uint16 // Optional
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToHTTPAnnounceRequest creates a new httptracker.AnnounceRequest from itself.
|
// ToHTTPAnnounceRequest creates a new httptracker.AnnounceRequest from itself.
|
||||||
func (ar AnnounceRequest) ToHTTPAnnounceRequest() httptracker.AnnounceRequest {
|
func (ar *AnnounceRequest) ToHTTPAnnounceRequest() *httptracker.AnnounceRequest {
|
||||||
var ip string
|
ip := "127.0.0.1"
|
||||||
if len(ar.IP) != 0 {
|
if PeerAddress != nil {
|
||||||
|
ip = PeerAddress.String()
|
||||||
|
} else if ar.IP != nil {
|
||||||
ip = ar.IP.String()
|
ip = ar.IP.String()
|
||||||
}
|
}
|
||||||
|
if ar.Port == 0 {
|
||||||
return httptracker.AnnounceRequest{
|
ar.Port = 6881
|
||||||
|
}
|
||||||
|
return &httptracker.AnnounceRequest{
|
||||||
InfoHash: ar.InfoHash,
|
InfoHash: ar.InfoHash,
|
||||||
PeerID: ar.PeerID,
|
PeerID: ar.PeerID,
|
||||||
Uploaded: ar.Uploaded,
|
Uploaded: ar.Uploaded,
|
||||||
@ -81,8 +85,11 @@ func (ar AnnounceRequest) ToHTTPAnnounceRequest() httptracker.AnnounceRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToUDPAnnounceRequest creates a new udptracker.AnnounceRequest from itself.
|
// ToUDPAnnounceRequest creates a new udptracker.AnnounceRequest from itself.
|
||||||
func (ar AnnounceRequest) ToUDPAnnounceRequest() udptracker.AnnounceRequest {
|
func (ar *AnnounceRequest) ToUDPAnnounceRequest() *udptracker.AnnounceRequest {
|
||||||
return udptracker.AnnounceRequest{
|
if PeerAddress != nil {
|
||||||
|
ar.IP = PeerAddress
|
||||||
|
}
|
||||||
|
return &udptracker.AnnounceRequest{
|
||||||
InfoHash: ar.InfoHash,
|
InfoHash: ar.InfoHash,
|
||||||
PeerID: ar.PeerID,
|
PeerID: ar.PeerID,
|
||||||
Downloaded: ar.Downloaded,
|
Downloaded: ar.Downloaded,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -22,14 +22,14 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
"github.com/xgfone/bt/tracker/udptracker"
|
"github.com/eyedeekay/go-i2p-bt/tracker/udptracker"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testHandler struct{}
|
type testHandler struct{}
|
||||||
|
|
||||||
func (testHandler) OnConnect(raddr *net.UDPAddr) (err error) { return }
|
func (testHandler) OnConnect(raddr net.Addr) (err error) { return }
|
||||||
func (testHandler) OnAnnounce(raddr *net.UDPAddr, req udptracker.AnnounceRequest) (
|
func (testHandler) OnAnnounce(raddr net.Addr, req udptracker.AnnounceRequest) (
|
||||||
r udptracker.AnnounceResponse, err error) {
|
r udptracker.AnnounceResponse, err error) {
|
||||||
if req.Port != 80 {
|
if req.Port != 80 {
|
||||||
err = errors.New("port is not 80")
|
err = errors.New("port is not 80")
|
||||||
@ -51,11 +51,11 @@ func (testHandler) OnAnnounce(raddr *net.UDPAddr, req udptracker.AnnounceRequest
|
|||||||
Interval: 1,
|
Interval: 1,
|
||||||
Leechers: 2,
|
Leechers: 2,
|
||||||
Seeders: 3,
|
Seeders: 3,
|
||||||
Addresses: []metainfo.Address{{IP: net.ParseIP("127.0.0.1"), Port: 8000}},
|
Addresses: []metainfo.Address{{IP: &net.IPAddr{IP: net.ParseIP("127.0.0.1")}, Port: 8000}},
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (testHandler) OnScrap(raddr *net.UDPAddr, infohashes []metainfo.Hash) (
|
func (testHandler) OnScrap(raddr net.Addr, infohashes []metainfo.Hash) (
|
||||||
rs []udptracker.ScrapeResponse, err error) {
|
rs []udptracker.ScrapeResponse, err error) {
|
||||||
rs = make([]udptracker.ScrapeResponse, len(infohashes))
|
rs = make([]udptracker.ScrapeResponse, len(infohashes))
|
||||||
for i := range infohashes {
|
for i := range infohashes {
|
||||||
@ -89,7 +89,7 @@ func ExampleClient() {
|
|||||||
|
|
||||||
// Send the ANNOUNCE request to the UDP tracker server,
|
// Send the ANNOUNCE request to the UDP tracker server,
|
||||||
// and get the ANNOUNCE response.
|
// and get the ANNOUNCE response.
|
||||||
req := AnnounceRequest{IP: net.ParseIP("127.0.0.1"), Port: 80}
|
req := AnnounceRequest{IP: &net.IPAddr{IP: net.ParseIP("127.0.0.1")}, Port: 80}
|
||||||
resp, err := client.Announce(context.Background(), req)
|
resp, err := client.Announce(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -25,7 +25,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/i2pkeys"
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProtocolID is magic constant for the udp tracker connection.
|
// ProtocolID is magic constant for the udp tracker connection.
|
||||||
@ -55,7 +56,7 @@ type AnnounceRequest struct {
|
|||||||
Uploaded int64
|
Uploaded int64
|
||||||
Event uint32
|
Event uint32
|
||||||
|
|
||||||
IP net.IP
|
IP net.Addr
|
||||||
Key int32
|
Key int32
|
||||||
NumWant int32 // -1 for default
|
NumWant int32 // -1 for default
|
||||||
Port uint16
|
Port uint16
|
||||||
@ -64,7 +65,7 @@ type AnnounceRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DecodeFrom decodes the request from b.
|
// DecodeFrom decodes the request from b.
|
||||||
func (r *AnnounceRequest) DecodeFrom(b []byte, ipv4 bool) {
|
func (r *AnnounceRequest) DecodeFrom(b []byte, w int) {
|
||||||
r.InfoHash = metainfo.NewHash(b[0:20])
|
r.InfoHash = metainfo.NewHash(b[0:20])
|
||||||
r.PeerID = metainfo.NewHash(b[20:40])
|
r.PeerID = metainfo.NewHash(b[20:40])
|
||||||
r.Downloaded = int64(binary.BigEndian.Uint64(b[40:48]))
|
r.Downloaded = int64(binary.BigEndian.Uint64(b[40:48]))
|
||||||
@ -72,13 +73,20 @@ func (r *AnnounceRequest) DecodeFrom(b []byte, ipv4 bool) {
|
|||||||
r.Uploaded = int64(binary.BigEndian.Uint64(b[56:64]))
|
r.Uploaded = int64(binary.BigEndian.Uint64(b[56:64]))
|
||||||
r.Event = binary.BigEndian.Uint32(b[64:68])
|
r.Event = binary.BigEndian.Uint32(b[64:68])
|
||||||
|
|
||||||
if ipv4 {
|
if w == 1 {
|
||||||
r.IP = make(net.IP, net.IPv4len)
|
ip := make(net.IP, net.IPv4len)
|
||||||
copy(r.IP, b[68:72])
|
copy(ip, b[68:72])
|
||||||
|
r.IP = &net.IPAddr{IP: ip}
|
||||||
b = b[72:]
|
b = b[72:]
|
||||||
|
} else if w == 0 {
|
||||||
|
ip := make(net.IP, net.IPv6len)
|
||||||
|
copy(ip, b[68:84])
|
||||||
|
r.IP = &net.IPAddr{IP: ip}
|
||||||
|
b = b[84:]
|
||||||
} else {
|
} else {
|
||||||
r.IP = make(net.IP, net.IPv6len)
|
ip := make(net.IP, 32)
|
||||||
copy(r.IP, b[68:84])
|
copy(ip, b[68:100])
|
||||||
|
r.IP, _ = i2pkeys.DestHashFromBytes(ip)
|
||||||
b = b[84:]
|
b = b[84:]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,10 +114,15 @@ func (r AnnounceRequest) EncodeTo(buf *bytes.Buffer) {
|
|||||||
binary.Write(buf, binary.BigEndian, r.Uploaded)
|
binary.Write(buf, binary.BigEndian, r.Uploaded)
|
||||||
binary.Write(buf, binary.BigEndian, r.Event)
|
binary.Write(buf, binary.BigEndian, r.Event)
|
||||||
|
|
||||||
if ip := r.IP.To4(); ip != nil {
|
if len(r.IP.String()) < 31 {
|
||||||
buf.Write(ip[:])
|
IP := net.ParseIP(r.IP.String())
|
||||||
|
if ip := IP.To4(); ip != nil {
|
||||||
|
buf.Write(ip[:])
|
||||||
|
} else {
|
||||||
|
buf.Write(IP[:])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
buf.Write(r.IP[:])
|
buf.Write([]byte(r.IP.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
binary.Write(buf, binary.BigEndian, r.Key)
|
binary.Write(buf, binary.BigEndian, r.Key)
|
||||||
@ -138,10 +151,10 @@ func (r AnnounceResponse) EncodeTo(buf *bytes.Buffer) {
|
|||||||
binary.Write(buf, binary.BigEndian, r.Leechers)
|
binary.Write(buf, binary.BigEndian, r.Leechers)
|
||||||
binary.Write(buf, binary.BigEndian, r.Seeders)
|
binary.Write(buf, binary.BigEndian, r.Seeders)
|
||||||
for _, addr := range r.Addresses {
|
for _, addr := range r.Addresses {
|
||||||
if ip := addr.IP.To4(); ip != nil {
|
if ip := addr.To4(); ip != nil {
|
||||||
buf.Write(ip[:])
|
buf.Write(ip.IP[:])
|
||||||
} else {
|
} else if ip := addr.To16(); ip != nil {
|
||||||
buf.Write(addr.IP[:])
|
buf.Write(ip.IP[:])
|
||||||
}
|
}
|
||||||
binary.Write(buf, binary.BigEndian, addr.Port)
|
binary.Write(buf, binary.BigEndian, addr.Port)
|
||||||
}
|
}
|
||||||
@ -166,7 +179,7 @@ func (r *AnnounceResponse) DecodeFrom(b []byte, ipv4 bool) {
|
|||||||
ip := make(net.IP, iplen)
|
ip := make(net.IP, iplen)
|
||||||
copy(ip, b[i-step:i-2])
|
copy(ip, b[i-step:i-2])
|
||||||
port := binary.BigEndian.Uint16(b[i-2 : i])
|
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{IP: &net.IPAddr{IP: ip}, Port: port})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -25,21 +25,23 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var Dial = net.Dial
|
||||||
|
|
||||||
// NewClientByDial returns a new Client by dialing.
|
// NewClientByDial returns a new Client by dialing.
|
||||||
func NewClientByDial(network, address string, c ...ClientConfig) (*Client, error) {
|
func NewClientByDial(network, address string, c ...ClientConfig) (*Client, error) {
|
||||||
conn, err := net.Dial(network, address)
|
conn, err := Dial(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewClient(conn.(*net.UDPConn), c...), nil
|
return NewClient(conn, c...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a new Client.
|
// NewClient returns a new Client.
|
||||||
func NewClient(conn *net.UDPConn, c ...ClientConfig) *Client {
|
func NewClient(conn net.Conn, c ...ClientConfig) *Client {
|
||||||
var conf ClientConfig
|
var conf ClientConfig
|
||||||
conf.set(c...)
|
conf.set(c...)
|
||||||
ipv4 := strings.Contains(conn.LocalAddr().String(), ".")
|
ipv4 := strings.Contains(conn.LocalAddr().String(), ".")
|
||||||
@ -74,7 +76,7 @@ func (c *ClientConfig) set(conf ...ClientConfig) {
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
ipv4 bool
|
ipv4 bool
|
||||||
conf ClientConfig
|
conf ClientConfig
|
||||||
conn *net.UDPConn
|
conn net.Conn
|
||||||
last time.Time
|
last time.Time
|
||||||
cid uint64
|
cid uint64
|
||||||
tid uint32
|
tid uint32
|
||||||
@ -115,6 +117,7 @@ func (utc *Client) parseError(b []byte) (tid uint32, reason string) {
|
|||||||
|
|
||||||
func (utc *Client) send(b []byte) (err error) {
|
func (utc *Client) send(b []byte) (err error) {
|
||||||
n, err := utc.conn.Write(b)
|
n, err := utc.conn.Write(b)
|
||||||
|
//n, err := utc.conn.WriteTo()
|
||||||
if err == nil && n < len(b) {
|
if err == nil && n < len(b) {
|
||||||
err = io.ErrShortWrite
|
err = io.ErrShortWrite
|
||||||
}
|
}
|
||||||
@ -228,15 +231,15 @@ func (utc *Client) announce(ctx context.Context, req AnnounceRequest) (
|
|||||||
// Announce sends a Announce request to the tracker.
|
// Announce sends a Announce request to the tracker.
|
||||||
//
|
//
|
||||||
// Notice:
|
// Notice:
|
||||||
// 1. if it does not connect to the UDP tracker server, it will connect to it,
|
// 1. if it does not connect to the UDP tracker server, it will connect to it,
|
||||||
// then send the ANNOUNCE request.
|
// then send the ANNOUNCE request.
|
||||||
// 2. If returning an error, you should retry it.
|
// 2. If returning an error, you should retry it.
|
||||||
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
||||||
func (utc *Client) Announce(c context.Context, r AnnounceRequest) (AnnounceResponse, error) {
|
func (utc *Client) Announce(c context.Context, r *AnnounceRequest) (AnnounceResponse, error) {
|
||||||
if r.PeerID.IsZero() {
|
if r.PeerID.IsZero() {
|
||||||
r.PeerID = utc.conf.ID
|
r.PeerID = utc.conf.ID
|
||||||
}
|
}
|
||||||
return utc.announce(c, r)
|
return utc.announce(c, *r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *Client) scrape(c context.Context, ihs []metainfo.Hash) (
|
func (utc *Client) scrape(c context.Context, ihs []metainfo.Hash) (
|
||||||
@ -299,10 +302,10 @@ func (utc *Client) scrape(c context.Context, ihs []metainfo.Hash) (
|
|||||||
// Scrape sends a Scrape request to the tracker.
|
// Scrape sends a Scrape request to the tracker.
|
||||||
//
|
//
|
||||||
// Notice:
|
// Notice:
|
||||||
// 1. if it does not connect to the UDP tracker server, it will connect to it,
|
// 1. if it does not connect to the UDP tracker server, it will connect to it,
|
||||||
// then send the ANNOUNCE request.
|
// then send the ANNOUNCE request.
|
||||||
// 2. If returning an error, you should retry it.
|
// 2. If returning an error, you should retry it.
|
||||||
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
||||||
func (utc *Client) Scrape(c context.Context, hs []metainfo.Hash) ([]ScrapeResponse, error) {
|
func (utc *Client) Scrape(c context.Context, hs []metainfo.Hash) ([]ScrapeResponse, error) {
|
||||||
return utc.scrape(c, hs)
|
return utc.scrape(c, hs)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -24,15 +24,15 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServerHandler is used to handle the request from the client.
|
// ServerHandler is used to handle the request from the client.
|
||||||
type ServerHandler interface {
|
type ServerHandler interface {
|
||||||
// OnConnect is used to check whether to make the connection or not.
|
// OnConnect is used to check whether to make the connection or not.
|
||||||
OnConnect(raddr *net.UDPAddr) (err error)
|
OnConnect(raddr net.Addr) (err error)
|
||||||
OnAnnounce(raddr *net.UDPAddr, req AnnounceRequest) (AnnounceResponse, error)
|
OnAnnounce(raddr net.Addr, req AnnounceRequest) (AnnounceResponse, error)
|
||||||
OnScrap(raddr *net.UDPAddr, infohashes []metainfo.Hash) ([]ScrapeResponse, error)
|
OnScrap(raddr net.Addr, infohashes []metainfo.Hash) ([]ScrapeResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeResponseHeader(buf *bytes.Buffer, action, tid uint32) {
|
func encodeResponseHeader(buf *bytes.Buffer, action, tid uint32) {
|
||||||
@ -41,7 +41,7 @@ func encodeResponseHeader(buf *bytes.Buffer, action, tid uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type wrappedPeerAddr struct {
|
type wrappedPeerAddr struct {
|
||||||
Addr *net.UDPAddr
|
Addr net.Addr
|
||||||
Time time.Time
|
Time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,16 +136,16 @@ func (uts *Server) Run() {
|
|||||||
} else if n < 16 {
|
} else if n < 16 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go uts.handleRequest(raddr.(*net.UDPAddr), buf, n)
|
go uts.handleRequest(raddr.(net.Addr), buf, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) handleRequest(raddr *net.UDPAddr, buf []byte, n int) {
|
func (uts *Server) handleRequest(raddr net.Addr, buf []byte, n int) {
|
||||||
defer uts.bufpool.Put(buf)
|
defer uts.bufpool.Put(buf)
|
||||||
uts.handlePacket(raddr, buf[:n])
|
uts.handlePacket(raddr, buf[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) send(raddr *net.UDPAddr, b []byte) {
|
func (uts *Server) send(raddr net.Addr, b []byte) {
|
||||||
n, err := uts.conn.WriteTo(b, raddr)
|
n, err := uts.conn.WriteTo(b, raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
uts.conf.ErrorLog("fail to send the udp tracker response to '%s': %s",
|
uts.conf.ErrorLog("fail to send the udp tracker response to '%s': %s",
|
||||||
@ -159,38 +159,38 @@ func (uts *Server) getConnectionID() uint64 {
|
|||||||
return atomic.AddUint64(&uts.cid, 1)
|
return atomic.AddUint64(&uts.cid, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) addConnection(cid uint64, raddr *net.UDPAddr) {
|
func (uts *Server) addConnection(cid uint64, raddr net.Addr) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
uts.lock.Lock()
|
uts.lock.Lock()
|
||||||
uts.conns[cid] = wrappedPeerAddr{Addr: raddr, Time: now}
|
uts.conns[cid] = wrappedPeerAddr{Addr: raddr, Time: now}
|
||||||
uts.lock.Unlock()
|
uts.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) checkConnection(cid uint64, raddr *net.UDPAddr) (ok bool) {
|
func (uts *Server) checkConnection(cid uint64, raddr net.Addr) (ok bool) {
|
||||||
uts.lock.RLock()
|
uts.lock.RLock()
|
||||||
if w, _ok := uts.conns[cid]; _ok && w.Addr.Port == raddr.Port &&
|
if w, _ok := uts.conns[cid]; _ok &&
|
||||||
bytes.Equal(w.Addr.IP, raddr.IP) {
|
w.Addr.String() == raddr.String() {
|
||||||
ok = true
|
ok = true
|
||||||
}
|
}
|
||||||
uts.lock.RUnlock()
|
uts.lock.RUnlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) sendError(raddr *net.UDPAddr, tid uint32, reason string) {
|
func (uts *Server) sendError(raddr net.Addr, tid uint32, reason string) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8+len(reason)))
|
buf := bytes.NewBuffer(make([]byte, 0, 8+len(reason)))
|
||||||
encodeResponseHeader(buf, ActionError, tid)
|
encodeResponseHeader(buf, ActionError, tid)
|
||||||
buf.WriteString(reason)
|
buf.WriteString(reason)
|
||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) sendConnResp(raddr *net.UDPAddr, tid uint32, cid uint64) {
|
func (uts *Server) sendConnResp(raddr net.Addr, tid uint32, cid uint64) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 16))
|
buf := bytes.NewBuffer(make([]byte, 0, 16))
|
||||||
encodeResponseHeader(buf, ActionConnect, tid)
|
encodeResponseHeader(buf, ActionConnect, tid)
|
||||||
binary.Write(buf, binary.BigEndian, cid)
|
binary.Write(buf, binary.BigEndian, cid)
|
||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) sendAnnounceResp(raddr *net.UDPAddr, tid uint32,
|
func (uts *Server) sendAnnounceResp(raddr net.Addr, tid uint32,
|
||||||
resp AnnounceResponse) {
|
resp AnnounceResponse) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8+12+len(resp.Addresses)*18))
|
buf := bytes.NewBuffer(make([]byte, 0, 8+12+len(resp.Addresses)*18))
|
||||||
encodeResponseHeader(buf, ActionAnnounce, tid)
|
encodeResponseHeader(buf, ActionAnnounce, tid)
|
||||||
@ -198,7 +198,7 @@ func (uts *Server) sendAnnounceResp(raddr *net.UDPAddr, tid uint32,
|
|||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) sendScrapResp(raddr *net.UDPAddr, tid uint32,
|
func (uts *Server) sendScrapResp(raddr net.Addr, tid uint32,
|
||||||
rs []ScrapeResponse) {
|
rs []ScrapeResponse) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8+len(rs)*12))
|
buf := bytes.NewBuffer(make([]byte, 0, 8+len(rs)*12))
|
||||||
encodeResponseHeader(buf, ActionScrape, tid)
|
encodeResponseHeader(buf, ActionScrape, tid)
|
||||||
@ -208,7 +208,7 @@ func (uts *Server) sendScrapResp(raddr *net.UDPAddr, tid uint32,
|
|||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *Server) handlePacket(raddr *net.UDPAddr, b []byte) {
|
func (uts *Server) handlePacket(raddr net.Addr, b []byte) {
|
||||||
cid := binary.BigEndian.Uint64(b[:8])
|
cid := binary.BigEndian.Uint64(b[:8])
|
||||||
action := binary.BigEndian.Uint32(b[8:12])
|
action := binary.BigEndian.Uint32(b[8:12])
|
||||||
tid := binary.BigEndian.Uint32(b[12:16])
|
tid := binary.BigEndian.Uint32(b[12:16])
|
||||||
@ -236,18 +236,24 @@ func (uts *Server) handlePacket(raddr *net.UDPAddr, b []byte) {
|
|||||||
switch action {
|
switch action {
|
||||||
case ActionAnnounce:
|
case ActionAnnounce:
|
||||||
var req AnnounceRequest
|
var req AnnounceRequest
|
||||||
if raddr.IP.To4() != nil { // For ipv4
|
if len(raddr.String()) < 16 { // For ipv4
|
||||||
if len(b) < 82 {
|
if len(b) < 82 {
|
||||||
uts.sendError(raddr, tid, "invalid announce request")
|
uts.sendError(raddr, tid, "invalid announce request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.DecodeFrom(b, true)
|
req.DecodeFrom(b, 1)
|
||||||
} else { // For ipv6
|
} else if len(raddr.String()) >= 16 && len(raddr.String()) < 32 { // for ipv6
|
||||||
if len(b) < 94 {
|
if len(b) < 94 {
|
||||||
uts.sendError(raddr, tid, "invalid announce request")
|
uts.sendError(raddr, tid, "invalid announce request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.DecodeFrom(b, false)
|
req.DecodeFrom(b, 0)
|
||||||
|
} else { // for cryptographic protocols
|
||||||
|
if len(b) < 110 {
|
||||||
|
uts.sendError(raddr, tid, "invalid announce request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.DecodeFrom(b, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := uts.handler.OnAnnounce(raddr, req)
|
resp, err := uts.handler.OnAnnounce(raddr, req)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -22,13 +22,13 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testHandler struct{}
|
type testHandler struct{}
|
||||||
|
|
||||||
func (testHandler) OnConnect(raddr *net.UDPAddr) (err error) { return }
|
func (testHandler) OnConnect(raddr net.Addr) (err error) { return }
|
||||||
func (testHandler) OnAnnounce(raddr *net.UDPAddr, req AnnounceRequest) (
|
func (testHandler) OnAnnounce(raddr net.Addr, req AnnounceRequest) (
|
||||||
r AnnounceResponse, err error) {
|
r AnnounceResponse, err error) {
|
||||||
if req.Port != 80 {
|
if req.Port != 80 {
|
||||||
err = errors.New("port is not 80")
|
err = errors.New("port is not 80")
|
||||||
@ -50,11 +50,11 @@ func (testHandler) OnAnnounce(raddr *net.UDPAddr, req AnnounceRequest) (
|
|||||||
Interval: 1,
|
Interval: 1,
|
||||||
Leechers: 2,
|
Leechers: 2,
|
||||||
Seeders: 3,
|
Seeders: 3,
|
||||||
Addresses: []metainfo.Address{{IP: net.ParseIP("127.0.0.1"), Port: 8001}},
|
Addresses: []metainfo.Address{{IP: &net.IPAddr{IP: net.ParseIP("127.0.0.1")}, Port: 8001}},
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (testHandler) OnScrap(raddr *net.UDPAddr, infohashes []metainfo.Hash) (
|
func (testHandler) OnScrap(raddr net.Addr, infohashes []metainfo.Hash) (
|
||||||
rs []ScrapeResponse, err error) {
|
rs []ScrapeResponse, err error) {
|
||||||
rs = make([]ScrapeResponse, len(infohashes))
|
rs = make([]ScrapeResponse, len(infohashes))
|
||||||
for i := range infohashes {
|
for i := range infohashes {
|
||||||
@ -89,7 +89,7 @@ func ExampleClient() {
|
|||||||
// Send the ANNOUNCE request to the UDP tracker server,
|
// Send the ANNOUNCE request to the UDP tracker server,
|
||||||
// and get the ANNOUNCE response.
|
// and get the ANNOUNCE response.
|
||||||
exts := []Extension{NewURLData([]byte("data")), NewNop()}
|
exts := []Extension{NewURLData([]byte("data")), NewNop()}
|
||||||
req := AnnounceRequest{IP: net.ParseIP("127.0.0.1"), Port: 80, Exts: exts}
|
req := &AnnounceRequest{IP: &net.IPAddr{IP: net.ParseIP("127.0.0.1")}, Port: 80, Exts: exts}
|
||||||
resp, err := client.Announce(context.Background(), req)
|
resp, err := client.Announce(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
1
uploader/torrent.go
Normal file
1
uploader/torrent.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package uploader
|
97
uploadhandler/block_upload_handler.go
Normal file
97
uploadhandler/block_upload_handler.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package blockdownload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/eyedeekay/go-i2p-bt/metainfo"
|
||||||
|
pp "github.com/eyedeekay/go-i2p-bt/peerprotocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlockUploadHandler is used to downloads the files in the torrent file.
|
||||||
|
type BlockUploadHandler struct {
|
||||||
|
pp.NoopHandler
|
||||||
|
pp.NoopBep3Handler
|
||||||
|
pp.NoopBep6Handler
|
||||||
|
|
||||||
|
Info metainfo.Info // Required
|
||||||
|
OnBlock func(index, offset uint32, b []byte) error // Required
|
||||||
|
RespondBlock func(c *pp.PeerConn) error // Required
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockUploadHandler returns a new BlockUploadHandler.
|
||||||
|
func NewBlockUploadHandler(info metainfo.Info, onBlock func(pieceIndex, pieceOffset uint32, b []byte) error, respondBlock func(c *pp.PeerConn) error) BlockUploadHandler {
|
||||||
|
return BlockUploadHandler{
|
||||||
|
Info: info,
|
||||||
|
OnBlock: onBlock,
|
||||||
|
RespondBlock: respondBlock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnHandShake implements the interface Handler#OnHandShake.
|
||||||
|
//
|
||||||
|
// Notice: it uses the field Data to store the inner data, you mustn't override
|
||||||
|
// it.
|
||||||
|
func (fd BlockUploadHandler) OnHandShake(c *pp.PeerConn) (err error) {
|
||||||
|
if err = c.SetUnchoked(); err == nil {
|
||||||
|
err = c.SetInterested()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ---------------------------------------------------------------------------
|
||||||
|
/// BEP 3
|
||||||
|
|
||||||
|
func (fd BlockUploadHandler) respond(pc *pp.PeerConn) (err error) {
|
||||||
|
if pc.PeerChoked {
|
||||||
|
err = pp.ErrChoked
|
||||||
|
} else {
|
||||||
|
err = fd.RespondBlock(pc)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Piece implements the interface Bep3Handler#Piece.
|
||||||
|
func (fd BlockUploadHandler) Piece(c *pp.PeerConn, i, b uint32, p []byte) (err error) {
|
||||||
|
if err = fd.OnBlock(i, b, p); err == nil {
|
||||||
|
err = fd.respond(c)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unchoke implements the interface Bep3Handler#Unchoke.
|
||||||
|
func (fd BlockUploadHandler) Unchoke(pc *pp.PeerConn) (err error) {
|
||||||
|
return fd.respond(pc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have implements the interface Bep3Handler#Have.
|
||||||
|
func (fd BlockUploadHandler) Have(pc *pp.PeerConn, index uint32) (err error) {
|
||||||
|
pc.BitField.Set(index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ---------------------------------------------------------------------------
|
||||||
|
/// BEP 6
|
||||||
|
|
||||||
|
// HaveAll implements the interface Bep6Handler#HaveAll.
|
||||||
|
func (fd BlockUploadHandler) HaveAll(pc *pp.PeerConn) (err error) {
|
||||||
|
pc.BitField = pp.NewBitField(fd.Info.CountPieces(), true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject implements the interface Bep6Handler#Reject.
|
||||||
|
func (fd BlockUploadHandler) Reject(pc *pp.PeerConn, index, begin, length uint32) (err error) {
|
||||||
|
pc.BitField.Unset(index)
|
||||||
|
return fd.respond(pc)
|
||||||
|
}
|
105
utils/addr.go
Normal file
105
utils/addr.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/eyedeekay/i2pkeys"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NetIPAddr(a net.Addr) net.IP {
|
||||||
|
switch a := a.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return a.IP
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return a.IP
|
||||||
|
case *i2pkeys.I2PAddr:
|
||||||
|
return net.ParseIP("127.0.0.1")
|
||||||
|
case i2pkeys.I2PAddr:
|
||||||
|
return net.ParseIP("127.0.0.1")
|
||||||
|
default:
|
||||||
|
ip, _, err := net.SplitHostPort(a.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return net.ParseIP(ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IPAddr(a net.Addr) string {
|
||||||
|
switch a := a.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return a.IP.String()
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return a.IP.String()
|
||||||
|
case *i2pkeys.I2PAddr:
|
||||||
|
return a.DestHash().Hash()
|
||||||
|
case i2pkeys.I2PAddr:
|
||||||
|
return a.DestHash().Hash()
|
||||||
|
default:
|
||||||
|
ip, _, err := net.SplitHostPort(a.String())
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Port(a net.Addr) int {
|
||||||
|
switch a := a.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return a.Port
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return a.Port
|
||||||
|
case *i2pkeys.I2PAddr:
|
||||||
|
return 6881
|
||||||
|
case i2pkeys.I2PAddr:
|
||||||
|
return 6881
|
||||||
|
default:
|
||||||
|
_, port, err := net.SplitHostPort(a.String())
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
iport, err := strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return iport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsIPv6Addr(addr net.Addr) bool {
|
||||||
|
switch xaddr := addr.(type) {
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return xaddr.IP.To4() == nil
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return xaddr.IP.To4() == nil
|
||||||
|
case *net.IPAddr:
|
||||||
|
return xaddr.IP.To4() == nil
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IpIsZero(ip net.IP) bool {
|
||||||
|
for _, b := range ip {
|
||||||
|
if b != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 xgfone
|
// Copyright 2020 xgfone, 2023 idk
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
Reference in New Issue
Block a user