From 7f3967a3ca94677c29ada448d7777411db4874d0 Mon Sep 17 00:00:00 2001 From: Alexander Sinitsyn Date: Fri, 15 Jan 2021 15:53:15 +0000 Subject: [PATCH] Hexadecimal ranges (#33) --- README.md | 6 ++++ convert/convert.go | 29 ++++++++++++++-- convert/convert_test.go | 74 +++++++++++++++++++++++++++++++++++------ go.sum | 1 + main.go | 8 +++-- 5 files changed, 102 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index edb59c3..30107fd 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ In addition, at least one of these is required: * -include-cidr - Include the network in CIDR format * -include-range - Include the IP range of the network in string format * -include-integer-range - Include the IP range of the network in integer format +* -include-hex-range - Include the IP range of the network in hexadecimal format Output ====== @@ -41,6 +42,11 @@ are string representations of the first and last IP address in the network. This adds `network_start_integer` and `network_last_integer` columns. These are integer representations of the first and last IP address in the network. +### Integer Range (-include-hex-range) + +This adds `network_start_hex` and `network_last_hex` columns. These +are hexadecimal representations of the first and last IP address in the network. + Copyright and License ===================== diff --git a/convert/convert.go b/convert/convert.go index 7dc7be5..75b41e6 100644 --- a/convert/convert.go +++ b/convert/convert.go @@ -20,7 +20,7 @@ type ( // ConvertFile converts the MaxMind GeoIP2 or GeoLite2 CSV file `inputFile` to // `outputFile` file using a different representation of the network. The // representation can be specified by setting one or more of `cidr`, -// `ipRange`, or `intRange` to true. If none of these are set to true, it will +// `ipRange`, `intRange` or `hexRange` to true. If none of these are set to true, it will // strip off the network information. func ConvertFile( // nolint: golint inputFile string, @@ -28,6 +28,7 @@ func ConvertFile( // nolint: golint cidr bool, ipRange bool, intRange bool, + hexRange bool, ) error { outFile, err := os.Create(outputFile) if err != nil { @@ -41,7 +42,7 @@ func ConvertFile( // nolint: golint } defer inFile.Close() // nolint: gosec - err = Convert(inFile, outFile, cidr, ipRange, intRange) + err = Convert(inFile, outFile, cidr, ipRange, intRange, hexRange) if err != nil { return err } @@ -62,10 +63,16 @@ func Convert( cidr bool, ipRange bool, intRange bool, + hexRange bool, ) error { makeHeader := func(orig []string) []string { return orig } makeLine := func(_ *ipaddr.Prefix, orig []string) []string { return orig } + if hexRange { + makeHeader = addHeaderFunc(makeHeader, hexRangeHeader) + makeLine = addLineFunc(makeLine, hexRangeLine) + } + if intRange { makeHeader = addHeaderFunc(makeHeader, intRangeHeader) makeLine = addLineFunc(makeLine, intRangeLine) @@ -133,6 +140,24 @@ func intRangeLine(network *ipaddr.Prefix, orig []string) []string { ) } +func hexRangeHeader(orig []string) []string { + return append([]string{"network_start_hex", "network_last_hex"}, orig...) +} + +func hexRangeLine(network *ipaddr.Prefix, orig []string) []string { + startInt := new(big.Int) + + startInt.SetBytes(canonicalizeIP(network.IP)) + + endInt := new(big.Int) + endInt.SetBytes(canonicalizeIP(network.Last())) + + return append( + []string{startInt.Text(16), endInt.Text(16)}, + orig..., + ) +} + func canonicalizeIP(ip net.IP) net.IP { if v4 := ip.To4(); v4 != nil { return v4 diff --git a/convert/convert_test.go b/convert/convert_test.go index e693f6f..f67fe9d 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -83,6 +83,31 @@ func TestIntRange(t *testing.T) { ) } +func TestHexRange(t *testing.T) { + checkHeader( + t, + hexRangeHeader, + []string{"network_start_hex", "network_last_hex"}, + ) + + checkLine( + t, + hexRangeLine, + "1.1.1.0/24", + []string{"1010100", "10101ff"}, + ) + + checkLine( + t, + hexRangeLine, + "2001:0db8:85a3:0042::/64", + []string{ + "20010db885a300420000000000000000", + "20010db885a30042ffffffffffffffff", + }, + ) +} + func checkHeader( t *testing.T, makeHeader headerFunc, @@ -120,6 +145,7 @@ func TestCIDROutput(t *testing.T) { true, false, false, + false, []interface{}{ "network", "1.0.0.0/24", @@ -139,6 +165,7 @@ func TestRangeOutput(t *testing.T) { false, true, false, + false, []interface{}{ "network_start_ip,network_last_ip", "1.0.0.0,1.0.0.255", @@ -158,6 +185,7 @@ func TestIntRangeOutput(t *testing.T) { false, false, true, + false, []interface{}{ "network_start_integer,network_last_integer", "16777216,16777471", @@ -170,6 +198,26 @@ func TestIntRangeOutput(t *testing.T) { ) } +func TestHexRangeOutput(t *testing.T) { + checkOutput( + t, + "hex range only", + false, + false, + false, + true, + []interface{}{ + "network_start_hex,network_last_hex", + "1000000,10000ff", + "4458c10,4458c17", + "53dc000,53dc7ff", + "20014220000000000000000000000000,20014220ffffffffffffffffffffffff", + "2402d000000000000000000000000000,2402d000ffffffffffffffffffffffff", + "24064000000000000000000000000000,24064000ffffffffffffffffffffffff", + }, + ) +} + func TestAllOutput(t *testing.T) { checkOutput( t, @@ -177,17 +225,19 @@ func TestAllOutput(t *testing.T) { true, true, true, + true, []interface{}{ - "network,network_start_ip,network_last_ip,network_start_integer,network_last_integer", - "1.0.0.0/24,1.0.0.0,1.0.0.255,16777216,16777471", - "4.69.140.16/29,4.69.140.16,4.69.140.23,71666704,71666711", - "5.61.192.0/21,5.61.192.0,5.61.199.255,87932928,87934975", // nolint: lll - "2001:4220::/32,2001:4220::,2001:4220:ffff:ffff:ffff:ffff:ffff:ffff,42541829336310884227257139937291534336,42541829415539046741521477530835484671", + "network,network_start_ip,network_last_ip,network_start_integer,network_last_integer,network_start_hex,network_last_hex", + "1.0.0.0/24,1.0.0.0,1.0.0.255,16777216,16777471,1000000,10000ff", + "4.69.140.16/29,4.69.140.16,4.69.140.23,71666704,71666711,4458c10,4458c17", + "5.61.192.0/21,5.61.192.0,5.61.199.255,87932928,87934975,53dc000,53dc7ff", // nolint: lll - "2402:d000::/32,2402:d000::,2402:d000:ffff:ffff:ffff:ffff:ffff:ffff,47866811183171600627242296191018336256,47866811262399763141506633784562286591", + "2001:4220::/32,2001:4220::,2001:4220:ffff:ffff:ffff:ffff:ffff:ffff,42541829336310884227257139937291534336,42541829415539046741521477530835484671,20014220000000000000000000000000,20014220ffffffffffffffffffffffff", // nolint: lll - "2406:4000::/32,2406:4000::,2406:4000:ffff:ffff:ffff:ffff:ffff:ffff,47884659703622814097215369772150030336,47884659782850976611479707365693980671", + "2402:d000::/32,2402:d000::,2402:d000:ffff:ffff:ffff:ffff:ffff:ffff,47866811183171600627242296191018336256,47866811262399763141506633784562286591,2402d000000000000000000000000000,2402d000ffffffffffffffffffffffff", + // nolint: lll + "2406:4000::/32,2406:4000::,2406:4000:ffff:ffff:ffff:ffff:ffff:ffff,47884659703622814097215369772150030336,47884659782850976611479707365693980671,24064000000000000000000000000000,24064000ffffffffffffffffffffffff", }, ) } @@ -198,6 +248,7 @@ func checkOutput( cidr bool, ipRange bool, intRange bool, + hexRange bool, expected []interface{}, ) { // nolint: lll @@ -211,7 +262,7 @@ func checkOutput( ` var outbuf bytes.Buffer - err := Convert(strings.NewReader(input), &outbuf, cidr, ipRange, intRange) + err := Convert(strings.NewReader(input), &outbuf, cidr, ipRange, intRange, hexRange) if err != nil { t.Fatal(err) } @@ -241,8 +292,9 @@ func TestFileWriting(t *testing.T) { 1.0.0.0/24,"some more" ` - expected := `network,network_start_ip,network_last_ip,network_start_integer,network_last_integer,something -1.0.0.0/24,1.0.0.0,1.0.0.255,16777216,16777471,some more + // nolint: lll + expected := `network,network_start_ip,network_last_ip,network_start_integer,network_last_integer,network_start_hex,network_last_hex,something +1.0.0.0/24,1.0.0.0,1.0.0.255,16777216,16777471,1000000,10000ff,some more ` inFile, err := ioutil.TempFile("", "input") @@ -260,7 +312,7 @@ func TestFileWriting(t *testing.T) { _, err = inFile.WriteString(input) require.NoError(t, err) - err = ConvertFile(inFile.Name(), outFile.Name(), true, true, true) + err = ConvertFile(inFile.Name(), outFile.Name(), true, true, true, true) if err != nil { t.Fatal(err) } diff --git a/go.sum b/go.sum index f6dcdca..1540156 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,7 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/main.go b/main.go index 064ee3e..7be7a1a 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ func main() { output := flag.String("output-file", "", "The path to the output CSV (REQUIRED)") ipRange := flag.Bool("include-range", false, "Include the IP range of the network in string format") intRange := flag.Bool("include-integer-range", false, "Include the IP range of the network in integer format") + hexRange := flag.Bool("include-hex-range", false, "Include the IP range of the network in hexadecimal format") cidr := flag.Bool("include-cidr", false, "Include the network in CIDR format") flag.Parse() @@ -28,8 +29,9 @@ func main() { errors = append(errors, "-output-file is required") } - if !*ipRange && !*intRange && !*cidr { - errors = append(errors, "-include-cidr, -include-range, or -include-integer-range is required") + if !*ipRange && !*intRange && !*cidr && !*hexRange { + errors = append(errors, "-include-cidr, -include-range, -include-integer-range,"+ + " or -include-hex-range is required") } args := flag.Args() @@ -42,7 +44,7 @@ func main() { os.Exit(1) } - err := convert.ConvertFile(*input, *output, *cidr, *ipRange, *intRange) + err := convert.ConvertFile(*input, *output, *cidr, *ipRange, *intRange, *hexRange) if err != nil { fmt.Fprintf(flag.CommandLine.Output(), "Error: %v\n", err) os.Exit(1)