@cryptotaxi247 / kubo / commits / 7cc73f7b8

add command to manipulate address filters and a sharness test for them

License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>

Jeromy committed Jun 30, 2015 at 18:25 UTC 7cc73f7b867469d8875e129f9acf6c41baeaeb85
9 files changed +403 -11
Godeps/Godeps.json
+1 -1
@@ -282,7 +282,7 @@
282 },
283 {
284 "ImportPath": "github.com/whyrusleeping/multiaddr-filter",
285 - "Rev": "15837fcc356fddef27c634b0f6379b3b7f259114"
285 + "Rev": "9e26222151125ecd3fc1fd190179b6bdd55f5608"
286 },
287 {
288 "ImportPath": "golang.org/x/crypto/blowfish",
Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/LICENSE new
+21
@@ -0,0 +1,21 @@
1 +The MIT License (MIT)
2 +
3 +Copyright (c) 2015 Jeromy Johnson
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in
13 +all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 +THE SOFTWARE.
Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/README.md new
+15
@@ -0,0 +1,15 @@
1 +# go-multiaddr-filter -- CIDR netmasks with multiaddr
2 +
3 +This module creates very simple [multiaddr](https://github.com/jbenet/go-multiaddr) formatted cidr netmasks.
4 +
5 +It doesn't do full multiaddr parsing to save on vendoring things and perf. The `net` package will take care of verifying the validity of the network part anyway.
6 +
7 +## Usage
8 +
9 +```go
10 +
11 +import filter "github.com/whyrusleeping/multiaddr-filter"
12 +
13 +filter.NewMask("/ip4/192.168.0.0/24") // ipv4
14 +filter.NewMask("/ip6/fe80::/64") // ipv6
15 +```
Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask.go
+35 -7
@@ -2,18 +2,46 @@ package mask
2
3 import (
4 "errors"
5 + "fmt"
6 "net"
7 "strings"
8 +
9 + manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
10 )
11
12 +var ErrInvalidFormat = errors.New("invalid multiaddr-filter format")
13 +
14 func NewMask(a string) (*net.IPNet, error) {
15 parts := strings.Split(a, "/")
11 - if len(parts) == 5 && parts[1] == "ip4" && parts[3] == "ipcidr" {
12 - _, ipn, err := net.ParseCIDR(parts[2] + "/" + parts[4])
13 - if err != nil {
14 - return nil, err
15 - }
16 - return ipn, nil
16 +
17 + if parts[0] != "" {
18 + return nil, ErrInvalidFormat
19 + }
20 +
21 + if len(parts) != 5 {
22 + return nil, ErrInvalidFormat
23 + }
24 +
25 + // check it's a valid filter address. ip + cidr
26 + isip := parts[1] == "ip4" || parts[1] == "ip6"
27 + iscidr := parts[3] == "ipcidr"
28 + if !isip || !iscidr {
29 + return nil, ErrInvalidFormat
30 }
18 - return nil, errors.New("invalid format")
31 +
32 + _, ipn, err := net.ParseCIDR(parts[2] + "/" + parts[4])
33 + if err != nil {
34 + return nil, err
35 + }
36 + return ipn, nil
37 +}
38 +
39 +func ConvertIPNet(n *net.IPNet) (string, error) {
40 + addr, err := manet.FromIP(n.IP)
41 + if err != nil {
42 + return "", err
43 + }
44 +
45 + b, _ := n.Mask.Size()
46 + return fmt.Sprintf("%s/ipcidr/%d", addr, b), nil
47 }
Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask_test.go
+96
@@ -5,6 +5,74 @@ import (
5 "testing"
6 )
7
8 +func TestValidMasks(t *testing.T) {
9 +
10 + cidrOrFatal := func(s string) *net.IPNet {
11 + _, ipn, err := net.ParseCIDR(s)
12 + if err != nil {
13 + t.Fatal(err)
14 + }
15 + return ipn
16 + }
17 +
18 + testCases := map[string]*net.IPNet{
19 + "/ip4/1.2.3.4/ipcidr/0": cidrOrFatal("1.2.3.4/0"),
20 + "/ip4/1.2.3.4/ipcidr/32": cidrOrFatal("1.2.3.4/32"),
21 + "/ip4/1.2.3.4/ipcidr/24": cidrOrFatal("1.2.3.4/24"),
22 + "/ip4/192.168.0.0/ipcidr/28": cidrOrFatal("192.168.0.0/28"),
23 + "/ip6/fe80::/ipcidr/0": cidrOrFatal("fe80::/0"),
24 + "/ip6/fe80::/ipcidr/64": cidrOrFatal("fe80::/64"),
25 + "/ip6/fe80::/ipcidr/128": cidrOrFatal("fe80::/128"),
26 + }
27 +
28 + for s, m1 := range testCases {
29 + m2, err := NewMask(s)
30 + if err != nil {
31 + t.Error("should be invalid:", s)
32 + continue
33 + }
34 +
35 + if m1.String() != m2.String() {
36 + t.Error("masks not equal:", m1, m2)
37 + }
38 + }
39 +
40 +}
41 +
42 +func TestInvalidMasks(t *testing.T) {
43 +
44 + testCases := []string{
45 + "/",
46 + "/ip4/10.1.2.3",
47 + "/ip6/::",
48 + "/ip4/1.2.3.4/cidr/24",
49 + "/ip6/fe80::/cidr/24",
50 + "/eth/aa:aa:aa:aa:aa/ipcidr/24",
51 + "foobar/ip4/1.2.3.4/ipcidr/32",
52 + }
53 +
54 + for _, s := range testCases {
55 + _, err := NewMask(s)
56 + if err != ErrInvalidFormat {
57 + t.Error("should be invalid:", s)
58 + }
59 + }
60 +
61 + testCases2 := []string{
62 + "/ip4/1.2.3.4/ipcidr/33",
63 + "/ip4/192.168.0.0/ipcidr/-1",
64 + "/ip6/fe80::/ipcidr/129",
65 + }
66 +
67 + for _, s := range testCases2 {
68 + _, err := NewMask(s)
69 + if err == nil {
70 + t.Error("should be invalid:", s)
71 + }
72 + }
73 +
74 +}
75 +
76 func TestFiltered(t *testing.T) {
77 var tests = map[string]map[string]bool{
78 "/ip4/10.0.0.0/ipcidr/8": map[string]bool{
@@ -34,3 +102,31 @@ func TestFiltered(t *testing.T) {
102 }
103 }
104 }
105 +
106 +func TestParsing(t *testing.T) {
107 + var addrs = map[string]string{
108 + "/ip4/192.168.0.0/ipcidr/16": "192.168.0.0/16",
109 + "/ip4/192.0.0.0/ipcidr/8": "192.0.0.0/8",
110 + "/ip6/2001:db8::/ipcidr/32": "2001:db8::/32",
111 + }
112 +
113 + for k, v := range addrs {
114 + m, err := NewMask(k)
115 + if err != nil {
116 + t.Fatal(err)
117 + }
118 +
119 + if m.String() != v {
120 + t.Fatalf("mask is wrong: ", m, v)
121 + }
122 +
123 + orig, err := ConvertIPNet(m)
124 + if err != nil {
125 + t.Fatal(err)
126 + }
127 +
128 + if orig != k {
129 + t.Fatal("backwards conversion failed: ", orig, k)
130 + }
131 + }
132 +}
core/commands/swarm.go
+143
@@ -9,10 +9,12 @@ import (
9 "sort"
10
11 cmds "github.com/ipfs/go-ipfs/commands"
12 + swarm "github.com/ipfs/go-ipfs/p2p/net/swarm"
13 peer "github.com/ipfs/go-ipfs/p2p/peer"
14 iaddr "github.com/ipfs/go-ipfs/util/ipfsaddr"
15
16 ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
17 + mafilter "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
18 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
19 )
20
@@ -32,6 +34,7 @@ ipfs swarm peers - List peers with open connections
34 ipfs swarm addrs - List known addresses. Useful to debug.
35 ipfs swarm connect <address> - Open connection to a given address
36 ipfs swarm disconnect <address> - Close connection to a given address
37 +ipfs swarm filters - Manipulate filters addresses
38 `,
39 ShortDescription: `
40 ipfs swarm is a tool to manipulate the network swarm. The swarm is the
@@ -44,6 +47,7 @@ ipfs peers in the internet.
47 "addrs": swarmAddrsCmd,
48 "connect": swarmConnectCmd,
49 "disconnect": swarmDisconnectCmd,
50 + "filters": swarmFiltersCmd,
51 },
52 }
53
@@ -358,3 +362,142 @@ func peersWithAddresses(addrs []string) (pis []peer.PeerInfo, err error) {
362 }
363 return pis, nil
364 }
365 +
366 +var swarmFiltersCmd = &cmds.Command{
367 + Helptext: cmds.HelpText{
368 + Tagline: "Manipulate address filters",
369 + ShortDescription: `
370 +'ipfs swarm filters' will list out currently applied filters. Its subcommands can be used
371 +to add or remove said filters. Filters are specified using the multiaddr-filter format:
372 +
373 +example:
374 +
375 + /ip4/192.168.0.0/ipcidr/16
376 +
377 +Where the above is equivalent to the standard CIDR:
378 +
379 + 192.168.0.0/16
380 +
381 +Filters default to those specified under the "DialBlocklist" config key.
382 +`,
383 + },
384 + Subcommands: map[string]*cmds.Command{
385 + "add": swarmFiltersAddCmd,
386 + "rm": swarmFiltersRmCmd,
387 + },
388 + Run: func(req cmds.Request, res cmds.Response) {
389 + n, err := req.Context().GetNode()
390 + if err != nil {
391 + res.SetError(err, cmds.ErrNormal)
392 + return
393 + }
394 +
395 + snet, ok := n.PeerHost.Network().(*swarm.Network)
396 + if !ok {
397 + res.SetError(errors.New("failed to cast network to swarm network"), cmds.ErrNormal)
398 + return
399 + }
400 +
401 + var output []string
402 + for _, f := range snet.Filters.Filters() {
403 + s, err := mafilter.ConvertIPNet(f)
404 + if err != nil {
405 + res.SetError(err, cmds.ErrNormal)
406 + return
407 + }
408 + output = append(output, s)
409 + }
410 + res.SetOutput(&stringList{output})
411 + },
412 + Marshalers: cmds.MarshalerMap{
413 + cmds.Text: stringListMarshaler,
414 + },
415 + Type: stringList{},
416 +}
417 +
418 +var swarmFiltersAddCmd = &cmds.Command{
419 + Helptext: cmds.HelpText{
420 + Tagline: "add an address filter",
421 + ShortDescription: `
422 +'ipfs swarm filters add' will add an address filter to the daemons swarm.
423 +Filters applied this way will not persist daemon reboots, to acheive that,
424 +add your filters to the ipfs config file.
425 +`,
426 + },
427 + Arguments: []cmds.Argument{
428 + cmds.StringArg("address", true, true, "multiaddr to filter").EnableStdin(),
429 + },
430 + Run: func(req cmds.Request, res cmds.Response) {
431 + n, err := req.Context().GetNode()
432 + if err != nil {
433 + res.SetError(err, cmds.ErrNormal)
434 + return
435 + }
436 +
437 + snet, ok := n.PeerHost.Network().(*swarm.Network)
438 + if !ok {
439 + res.SetError(errors.New("failed to cast network to swarm network"), cmds.ErrNormal)
440 + return
441 + }
442 +
443 + for _, arg := range req.Arguments() {
444 + mask, err := mafilter.NewMask(arg)
445 + if err != nil {
446 + res.SetError(err, cmds.ErrNormal)
447 + return
448 + }
449 +
450 + snet.Filters.AddDialFilter(mask)
451 + }
452 + },
453 +}
454 +
455 +var swarmFiltersRmCmd = &cmds.Command{
456 + Helptext: cmds.HelpText{
457 + Tagline: "remove an address filter",
458 + ShortDescription: `
459 +'ipfs swarm filters rm' will remove an address filter from the daemons swarm.
460 +Filters removed this way will not persist daemon reboots, to acheive that,
461 +remove your filters from the ipfs config file.
462 +`,
463 + },
464 + Arguments: []cmds.Argument{
465 + cmds.StringArg("address", true, true, "multiaddr filter to remove").EnableStdin(),
466 + },
467 + Run: func(req cmds.Request, res cmds.Response) {
468 + n, err := req.Context().GetNode()
469 + if err != nil {
470 + res.SetError(err, cmds.ErrNormal)
471 + return
472 + }
473 +
474 + if n.PeerHost == nil {
475 + res.SetError(errNotOnline, cmds.ErrNormal)
476 + return
477 + }
478 +
479 + snet, ok := n.PeerHost.Network().(*swarm.Network)
480 + if !ok {
481 + res.SetError(errors.New("failed to cast network to swarm network"), cmds.ErrNormal)
482 + return
483 + }
484 +
485 + if req.Arguments()[0] == "all" || req.Arguments()[0] == "*" {
486 + fs := snet.Filters.Filters()
487 + for _, f := range fs {
488 + snet.Filters.Remove(f)
489 + }
490 + return
491 + }
492 +
493 + for _, arg := range req.Arguments() {
494 + mask, err := mafilter.NewMask(arg)
495 + if err != nil {
496 + res.SetError(err, cmds.ErrNormal)
497 + return
498 + }
499 +
500 + snet.Filters.Remove(mask)
501 + }
502 + },
503 +}
p2p/net/filter/filter.go
+20 -2
@@ -9,11 +9,17 @@ import (
9 )
10
11 type Filters struct {
12 - filters []*net.IPNet
12 + filters map[string]*net.IPNet
13 +}
14 +
15 +func NewFilters() *Filters {
16 + return &Filters{
17 + filters: make(map[string]*net.IPNet),
18 + }
19 }
20
21 func (fs *Filters) AddDialFilter(f *net.IPNet) {
16 - fs.filters = append(fs.filters, f)
22 + fs.filters[f.String()] = f
23 }
24
25 func (f *Filters) AddrBlocked(a ma.Multiaddr) bool {
@@ -32,3 +38,15 @@ func (f *Filters) AddrBlocked(a ma.Multiaddr) bool {
38 }
39 return false
40 }
41 +
42 +func (f *Filters) Filters() []*net.IPNet {
43 + var out []*net.IPNet
44 + for _, ff := range f.filters {
45 + out = append(out, ff)
46 + }
47 + return out
48 +}
49 +
50 +func (f *Filters) Remove(ff *net.IPNet) {
51 + delete(f.filters, ff.String())
52 +}
p2p/net/swarm/swarm.go
+1 -1
@@ -84,7 +84,7 @@ func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
84 dialT: DialTimeout,
85 notifs: make(map[inet.Notifiee]ps.Notifiee),
86 bwc: bwc,
87 - Filters: new(filter.Filters),
87 + Filters: filter.NewFilters(),
88 }
89
90 // configure Swarm
test/sharness/t0141-addfilter.sh new
+71
@@ -0,0 +1,71 @@
1 +#!/bin/sh
2 +#
3 +# Copyright (c) 2014 Jeromy Johnson
4 +# MIT Licensed; see the LICENSE file in this repository.
5 +#
6 +
7 +test_description="Test ipfs swarm command"
8 +
9 +AF1="/ip4/192.168.0.0/ipcidr/16"
10 +AF2="/ip4/127.0.0.0/ipcidr/8"
11 +AF3="/ip6/2008:bcd::/ipcidr/32"
12 +AF4="/ip4/172.16.0.0/ipcidr/12"
13 +
14 +. lib/test-lib.sh
15 +
16 +test_init_ipfs
17 +
18 +test_swarm_filter_cmd() {
19 + printf "" > list_expected
20 + for AF in "$@"
21 + do
22 + echo "$AF" >>list_expected
23 + done
24 +
25 + test_expect_success "'ipfs swarm filters' succeeds" '
26 + ipfs swarm filters > list_actual
27 + '
28 +
29 + test_expect_success "'ipfs swarm filters' output looks good" '
30 + test_sort_cmp list_actual list_expected
31 + '
32 +}
33 +
34 +test_swarm_filters() {
35 +
36 + ipfs swarm filters rm all
37 +
38 + test_swarm_filter_cmd
39 +
40 + test_expect_success "'ipfs swarm filter add' succeeds" '
41 + ipfs swarm filters add $AF1 $AF2 $AF3
42 + '
43 +
44 + test_swarm_filter_cmd $AF1 $AF2 $AF3
45 +
46 + test_expect_success "'ipfs swarm filter rm' succeeds" '
47 + ipfs swarm filters rm $AF2 $AF3
48 + '
49 +
50 + test_swarm_filter_cmd $AF1
51 +
52 + test_expect_success "'ipfs swarm filter add' succeeds" '
53 + ipfs swarm filters add $AF4 $AF2
54 + '
55 +
56 + test_swarm_filter_cmd $AF1 $AF2 $AF4
57 +
58 + test_expect_success "'ipfs swarm filter rm' succeeds" '
59 + ipfs swarm filters rm $AF1 $AF2 $AF4
60 + '
61 +
62 + test_swarm_filter_cmd
63 +}
64 +
65 +test_launch_ipfs_daemon
66 +
67 +test_swarm_filters
68 +
69 +test_kill_ipfs_daemon
70 +
71 +test_done