Skip to content

Commit

Permalink
add ipcidr support (#177)
Browse files Browse the repository at this point in the history
* Add support for ipcidr protocol

* Move test

* Fix gocheck

* PR comments

* Check byte slice len
  • Loading branch information
MarcoPolo committed Jun 19, 2022
1 parent 7830bb7 commit f5adc3b
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 0 deletions.
4 changes: 4 additions & 0 deletions multiaddr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ func TestConstructFails(t *testing.T) {
"/ip4",
"/ip4/::1",
"/ip4/fdpsofodsajfdoisa",
"/ip4/::/ipcidr/256",
"/ip6/::/ipcidr/1026",
"/ip6",
"/ip6zone",
"/ip6zone/",
Expand Down Expand Up @@ -101,9 +103,11 @@ func TestConstructSucceeds(t *testing.T) {
cases := []string{
"/ip4/1.2.3.4",
"/ip4/0.0.0.0",
"/ip4/192.0.2.0/ipcidr/24",
"/ip6/::1",
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic",
"/ip6/2001:db8::/ipcidr/32",
"/ip6zone/x/ip6/fe80::1",
"/ip6zone/x%y/ip6/fe80::1",
"/ip6zone/x%y/ip6/::",
Expand Down
28 changes: 28 additions & 0 deletions net/convert.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package manet

import (
"errors"
"fmt"
"net"
"path/filepath"
Expand Down Expand Up @@ -51,6 +52,33 @@ func (cm *CodecMap) ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
return p(maddr)
}

// MultiaddrToIPNet converts a multiaddr to an IPNet. Useful for seeing if another IP address is contained within this multiaddr network+mask
func MultiaddrToIPNet(m ma.Multiaddr) (*net.IPNet, error) {
var ipString string
var mask string

ma.ForEach(m, func(c ma.Component) bool {
if c.Protocol().Code == ma.P_IP4 || c.Protocol().Code == ma.P_IP6 {
ipString = c.Value()
}
if c.Protocol().Code == ma.P_IPCIDR {
mask = c.Value()
}
return ipString == "" || mask == ""
})

if ipString == "" {
return nil, errors.New("no ip protocol found")
}

if mask == "" {
return nil, errors.New("no mask found")
}

_, ipnet, err := net.ParseCIDR(ipString + "/" + string(mask))
return ipnet, err
}

func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) {
network, host, err := DialArgs(maddr)
if err != nil {
Expand Down
62 changes: 62 additions & 0 deletions net/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,65 @@ func TestDialArgs(t *testing.T) {
test("/dns6/abc.com/udp/1234", "udp6", "abc.com:1234") // DNS6:port
test("/dns6/abc.com", "ip6", "abc.com") // Just DNS6
}

func TestMultiaddrToIPNet(t *testing.T) {
type testCase struct {
name string
ma string
ips []string
contained []bool
}

testCases := []testCase{
{
name: "basic",
ma: "/ip4/1.2.3.0/ipcidr/24",
ips: []string{"1.2.3.4", "1.2.3.9", "2.1.1.1"},
contained: []bool{true, true, false},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ma := ma.StringCast(tc.ma)

ipnet, err := MultiaddrToIPNet(ma)
if err != nil {
t.Fatalf("failed to parse multiaddr %v into ipnet", ma)
}
for i, ipString := range tc.ips {
ip := net.ParseIP(ipString)
if ip == nil {
t.Fatalf("failed to parse IP %s", ipString)
}
if ipnet.Contains(ip) != tc.contained[i] {
t.Fatalf("Contains check failed. Expected %v got %v", tc.contained[i], ipnet.Contains(ip))
}
}
})
}
}

func TestFailMultiaddrToIPNet(t *testing.T) {
type testCase struct {
name string
ma string
}

testCases := []testCase{
{name: "missing ip addr", ma: "/ipcidr/24"},
{name: "wrong mask", ma: "/ip4/1.2.3.0/ipcidr/128"},
{name: "wrong mask", ma: "/ip6/::/ipcidr/255"},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ma := ma.StringCast(tc.ma)

_, err := MultiaddrToIPNet(ma)
if err == nil {
t.Fatalf("Expected error when parsing: %s", tc.ma)
}
})
}
}
9 changes: 9 additions & 0 deletions protocols.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
P_DCCP = 0x0021
P_IP6 = 0x0029
P_IP6ZONE = 0x002A
P_IPCIDR = 0x002B
P_QUIC = 0x01CC
P_SCTP = 0x0084
P_CIRCUIT = 0x0122
Expand Down Expand Up @@ -103,6 +104,13 @@ var (
Size: 128,
Transcoder: TranscoderIP6,
}
protoIPCIDR = Protocol{
Name: "ipcidr",
Code: P_IPCIDR,
VCode: CodeToVarint(P_IPCIDR),
Size: 8,
Transcoder: TranscoderIPCIDR,
}
// these require varint
protoIP6ZONE = Protocol{
Name: "ip6zone",
Expand Down Expand Up @@ -239,6 +247,7 @@ func init() {
protoDCCP,
protoIP6,
protoIP6ZONE,
protoIPCIDR,
protoSCTP,
protoCIRCUIT,
protoONION2,
Expand Down
16 changes: 16 additions & 0 deletions transcoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ func (t twrp) ValidateBytes(b []byte) error {
var TranscoderIP4 = NewTranscoderFromFunctions(ip4StB, ip4BtS, nil)
var TranscoderIP6 = NewTranscoderFromFunctions(ip6StB, ip6BtS, nil)
var TranscoderIP6Zone = NewTranscoderFromFunctions(ip6zoneStB, ip6zoneBtS, ip6zoneVal)
var TranscoderIPCIDR = NewTranscoderFromFunctions(ipcidrStB, ipcidrBtS, nil)

func ipcidrBtS(b []byte) (string, error) {
if len(b) != 1 {
return "", fmt.Errorf("invalid length (should be == 1)")
}
return strconv.Itoa(int(b[0])), nil
}

func ipcidrStB(s string) ([]byte, error) {
ipMask, err := strconv.ParseUint(s, 10, 8)
if err != nil {
return nil, err
}
return []byte{byte(uint8(ipMask))}, nil
}

func ip4StB(s string) ([]byte, error) {
i := net.ParseIP(s).To4()
Expand Down

0 comments on commit f5adc3b

Please sign in to comment.