mathRand "math/rand"
"net"
"os"
+ "path/filepath"
"sort"
"strings"
"sync"
"github.com/anacrolix/libtorgo/metainfo"
"bitbucket.org/anacrolix/go.torrent/dht"
+ "bitbucket.org/anacrolix/go.torrent/iplist"
pp "bitbucket.org/anacrolix/go.torrent/peer_protocol"
"bitbucket.org/anacrolix/go.torrent/tracker"
_ "bitbucket.org/anacrolix/go.torrent/tracker/udp"
. "bitbucket.org/anacrolix/go.torrent/util"
- "bitbucket.org/anacrolix/go.torrent/util/levelmu"
)
var (
downloadStrategy DownloadStrategy
dHT *dht.Server
disableUTP bool
+ ipBlockList *iplist.IPList
- mu levelmu.LevelMutex
+ mu sync.RWMutex
event sync.Cond
quit chan struct{}
dataWaits map[*torrent][]dataWait
}
+func (me *Client) SetIPBlockList(list *iplist.IPList) {
+ me.mu.Lock()
+ defer me.mu.Unlock()
+ me.ipBlockList = list
+}
+
func (me *Client) PeerID() string {
return string(me.peerID[:])
}
}
func (cl *Client) WriteStatus(_w io.Writer) {
- cl.mu.LevelLock(1)
- defer cl.mu.Unlock()
+ cl.mu.RLock()
+ defer cl.mu.RUnlock()
w := bufio.NewWriter(_w)
defer w.Flush()
fmt.Fprintf(w, "Listening on %s\n", cl.ListenAddr())
// Read torrent data at the given offset. Returns ErrDataNotReady if the data
// isn't available.
func (cl *Client) TorrentReadAt(ih InfoHash, off int64, p []byte) (n int, err error) {
- cl.mu.LevelLock(1)
- defer cl.mu.Unlock()
+ cl.mu.RLock()
+ defer cl.mu.RUnlock()
t := cl.torrent(ih)
if t == nil {
err = errors.New("unknown torrent")
return t.Data.ReadAt(p, off)
}
+func (cl *Client) setEnvBlocklist() (err error) {
+ filename := os.Getenv("TORRENT_BLOCKLIST_FILE")
+ defaultBlocklist := filename == ""
+ if defaultBlocklist {
+ filename = filepath.Join(os.Getenv("HOME"), ".config/torrent/blocklist")
+ }
+ f, err := os.Open(filename)
+ if err != nil {
+ if defaultBlocklist {
+ err = nil
+ }
+ return
+ }
+ defer f.Close()
+ var ranges []iplist.Range
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ r, ok, lineErr := iplist.ParseBlocklistP2PLine(scanner.Text())
+ if lineErr != nil {
+ err = fmt.Errorf("error reading torrent blocklist line: %s", lineErr)
+ return
+ }
+ if !ok {
+ continue
+ }
+ ranges = append(ranges, r)
+ }
+ err = scanner.Err()
+ if err != nil {
+ err = fmt.Errorf("error reading torrent blocklist: %s", err)
+ return
+ }
+ cl.ipBlockList = iplist.New(ranges)
+ return
+}
+
func NewClient(cfg *Config) (cl *Client, err error) {
if cfg == nil {
cfg = &Config{}
dataWaits: make(map[*torrent][]dataWait),
}
cl.event.L = &cl.mu
- cl.mu.Init(2)
+
+ err = cl.setEnvBlocklist()
+ if err != nil {
+ return
+ }
if cfg.PeerID != "" {
CopyExact(&cl.peerID, cfg.PeerID)
dhtCfg.Conn = utpL.RawConn
}
cl.dHT, err = dht.NewServer(dhtCfg)
+ if cl.ipBlockList != nil {
+ cl.dHT.SetIPBlockList(cl.ipBlockList)
+ }
if err != nil {
return
}
me.mu.Unlock()
}
+func (cl *Client) ipBlocked(ip net.IP) bool {
+ if cl.ipBlockList == nil {
+ return false
+ }
+ if r := cl.ipBlockList.Lookup(ip); r != nil {
+ log.Printf("IP blocked: %s in range %s-%s: %s", ip, r.First, r.Last, r.Description)
+ return true
+ }
+ return false
+}
+
func (cl *Client) acceptConnections(l net.Listener, utp bool) {
for {
- // We accept all connections immediately, because we don't what
+ // We accept all connections immediately, because we don't know what
// torrent they're for.
conn, err := l.Accept()
select {
return
}
acceptedConns.Add(1)
+ cl.mu.RLock()
+ blocked := cl.ipBlocked(AddrIP(conn.RemoteAddr()))
+ cl.mu.RUnlock()
+ if blocked {
+ continue
+ }
go func() {
if err := cl.runConnection(conn, nil, peerSourceIncoming, utp); err != nil {
log.Print(err)
duplicateConnsAvoided.Add(1)
return
}
+ if me.ipBlocked(peer.IP) {
+ return
+ }
dialTimeout := reducedDialTimeout(nominalDialTimeout, me.halfOpenLimit, len(t.Peers))
t.HalfOpen[addr] = struct{}{}
go func() {
"sync"
"time"
+ "bitbucket.org/anacrolix/go.torrent/iplist"
+
"bitbucket.org/anacrolix/go.torrent/logonce"
"bitbucket.org/anacrolix/go.torrent/util"
"github.com/anacrolix/libtorgo/bencode"
mu sync.Mutex
closed chan struct{}
passive bool // Don't respond to queries.
+ ipBlockList *iplist.IPList
NumConfirmedAnnounces int
}
go func() {
err := s.bootstrap()
if err != nil {
- panic(err)
+ log.Printf("error bootstrapping DHT: %s", err)
}
}()
return
return
}
+func (s *Server) SetIPBlockList(list *iplist.IPList) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.ipBlockList = list
+}
+
func (s *Server) init() (err error) {
err = s.setDefaults()
if err != nil {
}
}
+func (s *Server) ipBlocked(ip net.IP) bool {
+ if s.ipBlockList == nil {
+ return false
+ }
+ return s.ipBlockList.Lookup(ip) != nil
+}
+
func (s *Server) AddNode(ni NodeInfo) {
s.mu.Lock()
defer s.mu.Unlock()
}
err = s.writeToNode(b, addr)
if err != nil {
- panic(err)
+ log.Printf("error replying to %s: %s", addr, err)
}
}
}
func (s *Server) writeToNode(b []byte, node dHTAddr) (err error) {
+ if list := s.ipBlockList; list != nil {
+ if r := list.Lookup(util.AddrIP(node)); r != nil {
+ err = fmt.Errorf("write to %s blocked: %s", node, r.Description)
+ return
+ }
+ }
n, err := s.socket.WriteTo(b, node)
if err != nil {
err = fmt.Errorf("error writing %d bytes to %s: %s", len(b), node, err)
// TODO: Why would people even do this?
continue
}
+ if s.ipBlocked(util.AddrIP(cni.Addr)) {
+ continue
+ }
n := s.getNode(cni.Addr)
n.id = string(cni.ID[:])
}