From 0bf6b39cafda5fb243f8ae3c909650077c6ecab3 Mon Sep 17 00:00:00 2001
From: Jeromy <jeromyj@gmail.com>
Date: Wed, 17 Jun 2015 16:26:45 -0700
Subject: [PATCH] filter incoming connections and add a test of functionality

- add extra check to dialblock test
- move filter to separate package
- also improved tests
- sunk filters down into p2p/net/conn/listener

License: MIT
Signed-off-by: Jeromy <jeromyj@gmail.com>
Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
---
 p2p/net/conn/interface.go     |  3 ++
 p2p/net/conn/listen.go        | 13 ++++++++
 p2p/net/filter/filter.go      | 34 ++++++++++++++++++++
 p2p/net/swarm/swarm.go        | 31 ++-----------------
 p2p/net/swarm/swarm_dial.go   |  8 ++++-
 p2p/net/swarm/swarm_listen.go |  2 ++
 p2p/net/swarm/swarm_test.go   | 58 +++++++++++++++++++++++++++++++++++
 7 files changed, 120 insertions(+), 29 deletions(-)
 create mode 100644 p2p/net/filter/filter.go

diff --git a/p2p/net/conn/interface.go b/p2p/net/conn/interface.go
index 7d2c95af1..3a61911af 100644
--- a/p2p/net/conn/interface.go
+++ b/p2p/net/conn/interface.go
@@ -7,6 +7,7 @@ import (
 
 	key "github.com/ipfs/go-ipfs/blocks/key"
 	ic "github.com/ipfs/go-ipfs/p2p/crypto"
+	filter "github.com/ipfs/go-ipfs/p2p/net/filter"
 	peer "github.com/ipfs/go-ipfs/p2p/peer"
 
 	msgio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio"
@@ -86,6 +87,8 @@ type Listener interface {
 	// LocalPeer is the identity of the local Peer.
 	LocalPeer() peer.ID
 
+	SetAddrFilters(*filter.Filters)
+
 	// Close closes the listener.
 	// Any blocked Accept operations will be unblocked and return errors.
 	Close() error
diff --git a/p2p/net/conn/listen.go b/p2p/net/conn/listen.go
index d60c0ba3a..ea91e5a56 100644
--- a/p2p/net/conn/listen.go
+++ b/p2p/net/conn/listen.go
@@ -13,6 +13,7 @@ import (
 	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
 
 	ic "github.com/ipfs/go-ipfs/p2p/crypto"
+	filter "github.com/ipfs/go-ipfs/p2p/net/filter"
 	peer "github.com/ipfs/go-ipfs/p2p/peer"
 )
 
@@ -26,6 +27,8 @@ type listener struct {
 	local peer.ID    // LocalPeer is the identity of the local Peer
 	privk ic.PrivKey // private key to use to initialize secure conns
 
+	filters *filter.Filters
+
 	wrapper ConnWrapper
 
 	cg ctxgroup.ContextGroup
@@ -45,6 +48,10 @@ func (l *listener) String() string {
 	return fmt.Sprintf("<Listener %s %s>", l.local, l.Multiaddr())
 }
 
+func (l *listener) SetAddrFilters(fs *filter.Filters) {
+	l.filters = fs
+}
+
 // Accept waits for and returns the next connection to the listener.
 // Note that unfortunately this
 func (l *listener) Accept() (net.Conn, error) {
@@ -81,6 +88,12 @@ func (l *listener) Accept() (net.Conn, error) {
 		}
 
 		log.Debugf("listener %s got connection: %s <---> %s", l, maconn.LocalMultiaddr(), maconn.RemoteMultiaddr())
+
+		if l.filters != nil && l.filters.AddrBlocked(maconn.RemoteMultiaddr()) {
+			log.Debugf("blocked connection from %s", maconn.RemoteMultiaddr())
+			maconn.Close()
+			continue
+		}
 		// If we have a wrapper func, wrap this conn
 		if l.wrapper != nil {
 			maconn = l.wrapper(maconn)
diff --git a/p2p/net/filter/filter.go b/p2p/net/filter/filter.go
new file mode 100644
index 000000000..1642a8b77
--- /dev/null
+++ b/p2p/net/filter/filter.go
@@ -0,0 +1,34 @@
+package filter
+
+import (
+	"net"
+	"strings"
+
+	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
+	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
+)
+
+type Filters struct {
+	filters []*net.IPNet
+}
+
+func (fs *Filters) AddDialFilter(f *net.IPNet) {
+	fs.filters = append(fs.filters, f)
+}
+
+func (f *Filters) AddrBlocked(a ma.Multiaddr) bool {
+	_, addr, err := manet.DialArgs(a)
+	if err != nil {
+		// if we cant parse it, its probably not blocked
+		return false
+	}
+
+	ipstr := strings.Split(addr, ":")[0]
+	ip := net.ParseIP(ipstr)
+	for _, ft := range f.filters {
+		if ft.Contains(ip) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/p2p/net/swarm/swarm.go b/p2p/net/swarm/swarm.go
index 814fc7157..4fe45df85 100644
--- a/p2p/net/swarm/swarm.go
+++ b/p2p/net/swarm/swarm.go
@@ -4,19 +4,18 @@ package swarm
 
 import (
 	"fmt"
-	"net"
 	"sync"
 	"time"
 
 	metrics "github.com/ipfs/go-ipfs/metrics"
 	inet "github.com/ipfs/go-ipfs/p2p/net"
+	filter "github.com/ipfs/go-ipfs/p2p/net/filter"
 	addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
 	peer "github.com/ipfs/go-ipfs/p2p/peer"
 	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
 
 	ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
 	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
-	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
 	ps "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
 	pst "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream/transport"
 	psy "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream/transport/yamux"
@@ -53,7 +52,7 @@ type Swarm struct {
 	notifs  map[inet.Notifiee]ps.Notifiee
 
 	// filters for addresses that shouldnt be dialed
-	Filters *Filters
+	Filters *filter.Filters
 
 	cg  ctxgroup.ContextGroup
 	bwc metrics.Reporter
@@ -76,7 +75,7 @@ func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
 		dialT:   DialTimeout,
 		notifs:  make(map[inet.Notifiee]ps.Notifiee),
 		bwc:     bwc,
-		Filters: new(Filters),
+		Filters: new(filter.Filters),
 	}
 
 	// configure Swarm
@@ -90,30 +89,6 @@ func (s *Swarm) teardown() error {
 	return s.swarm.Close()
 }
 
-type Filters struct {
-	filters []*net.IPNet
-}
-
-func (fs *Filters) AddDialFilter(f *net.IPNet) {
-	fs.filters = append(fs.filters, f)
-}
-
-func (f *Filters) AddrBlocked(a ma.Multiaddr) bool {
-	_, addr, err := manet.DialArgs(a)
-	if err != nil {
-		// if we cant parse it, its probably not blocked
-		return false
-	}
-
-	ip := net.ParseIP(addr)
-	for _, ft := range f.filters {
-		if ft.Contains(ip) {
-			return true
-		}
-	}
-	return false
-}
-
 // CtxGroup returns the Context Group of the swarm
 func filterAddrs(listenAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
 	if len(listenAddrs) > 0 {
diff --git a/p2p/net/swarm/swarm_dial.go b/p2p/net/swarm/swarm_dial.go
index 4fe2bc126..c969ca570 100644
--- a/p2p/net/swarm/swarm_dial.go
+++ b/p2p/net/swarm/swarm_dial.go
@@ -303,7 +303,6 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
 	ila, _ := s.InterfaceListenAddresses()
 	remoteAddrs = addrutil.Subtract(remoteAddrs, ila)
 	remoteAddrs = addrutil.Subtract(remoteAddrs, s.peers.Addrs(s.local))
-	remoteAddrs = s.filterAddrs(remoteAddrs)
 
 	log.Debugf("%s swarm dialing %s -- local:%s remote:%s", s.local, p, s.ListenAddresses(), remoteAddrs)
 	if len(remoteAddrs) == 0 {
@@ -312,6 +311,13 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
 		return nil, err
 	}
 
+	remoteAddrs = s.filterAddrs(remoteAddrs)
+	if len(remoteAddrs) == 0 {
+		err := errors.New("all adresses for peer have been filtered out")
+		logdial["error"] = err
+		return nil, err
+	}
+
 	// open connection to peer
 	d := &conn.Dialer{
 		Dialer: manet.Dialer{
diff --git a/p2p/net/swarm/swarm_listen.go b/p2p/net/swarm/swarm_listen.go
index 2a985763f..4a8f4dd4d 100644
--- a/p2p/net/swarm/swarm_listen.go
+++ b/p2p/net/swarm/swarm_listen.go
@@ -69,6 +69,8 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
 		return err
 	}
 
+	list.SetAddrFilters(s.Filters)
+
 	if cw, ok := list.(conn.ListenerConnWrapper); ok {
 		cw.SetConnWrapper(func(c manet.Conn) manet.Conn {
 			return mconn.WrapConn(s.bwc, c)
diff --git a/p2p/net/swarm/swarm_test.go b/p2p/net/swarm/swarm_test.go
index 9aa825320..4c12a1b5d 100644
--- a/p2p/net/swarm/swarm_test.go
+++ b/p2p/net/swarm/swarm_test.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"io"
+	"net"
 	"sync"
 	"testing"
 	"time"
@@ -270,3 +271,60 @@ func TestConnHandler(t *testing.T) {
 	default:
 	}
 }
+
+func TestAddrBlocking(t *testing.T) {
+	ctx := context.Background()
+	swarms := makeSwarms(ctx, t, 2)
+
+	swarms[0].SetConnHandler(func(conn *Conn) {
+		t.Fatal("no connections should happen!")
+	})
+
+	_, block, err := net.ParseCIDR("127.0.0.1/8")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	swarms[1].Filters.AddDialFilter(block)
+
+	swarms[1].peers.AddAddr(swarms[0].LocalPeer(), swarms[0].ListenAddresses()[0], peer.PermanentAddrTTL)
+	_, err = swarms[1].Dial(context.TODO(), swarms[0].LocalPeer())
+	if err == nil {
+		t.Fatal("dial should have failed")
+	}
+
+	swarms[0].peers.AddAddr(swarms[1].LocalPeer(), swarms[1].ListenAddresses()[0], peer.PermanentAddrTTL)
+	_, err = swarms[0].Dial(context.TODO(), swarms[1].LocalPeer())
+	if err == nil {
+		t.Fatal("dial should have failed")
+	}
+}
+
+func TestFilterBounds(t *testing.T) {
+	ctx := context.Background()
+	swarms := makeSwarms(ctx, t, 2)
+
+	conns := make(chan struct{}, 8)
+	swarms[0].SetConnHandler(func(conn *Conn) {
+		conns <- struct{}{}
+	})
+
+	// Address that we wont be dialing from
+	_, block, err := net.ParseCIDR("192.0.0.1/8")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// set filter on both sides, shouldnt matter
+	swarms[1].Filters.AddDialFilter(block)
+	swarms[0].Filters.AddDialFilter(block)
+
+	connectSwarms(t, ctx, swarms)
+
+	select {
+	case <-time.After(time.Second):
+		t.Fatal("should have gotten connection")
+	case <-conns:
+		fmt.Println("got connect")
+	}
+}
-- 
GitLab