about summary refs log tree commit diff
path: root/tools/ipconverter
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2022-06-18 14:10:23 -0700
committerFranck Cuny <franck@fcuny.net>2022-06-18 14:18:02 -0700
commit300eb227b160f05d7aa429e9c779f2c74e095f81 (patch)
tree8b0ea3e46f6d259969aef51eec2e20d385fecfa7 /tools/ipconverter
parentref(scripts): remove the module for scripts (diff)
downloadworld-300eb227b160f05d7aa429e9c779f2c74e095f81.tar.gz
feat(ipconverter): add a tool to convert IPv4 to int and vice-versa
It's sometimes useful to store IPv4 addresses as an integer. This tool
helps with the conversion, and also does the reverse conversion.

```
% go run . int2ip 3232235521
3232235521      192.168.0.1
% go run . ip2int 192.168.0.1
192.168.0.1     3232235521
```

Change-Id: Ic1e44057bca3539b4c183d387c635f69f5bf3f36
Reviewed-on: https://cl.fcuny.net/c/world/+/441
Tested-by: CI
Reviewed-by: Franck Cuny <franck@fcuny.net>
Diffstat (limited to 'tools/ipconverter')
-rw-r--r--tools/ipconverter/default.nix15
-rw-r--r--tools/ipconverter/go.mod3
-rw-r--r--tools/ipconverter/main.go58
-rw-r--r--tools/ipconverter/main_test.go44
4 files changed, 120 insertions, 0 deletions
diff --git a/tools/ipconverter/default.nix b/tools/ipconverter/default.nix
new file mode 100644
index 0000000..3be25fc
--- /dev/null
+++ b/tools/ipconverter/default.nix
@@ -0,0 +1,15 @@
+{ pkgs, ... }:
+
+pkgs.buildGoModule rec {
+  name = "ipconverter";
+  src = ./.;
+  vendorSha256 = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=";
+  nativeBuildInputs = with pkgs; [ go ];
+
+  meta = with pkgs.lib; {
+    description = "CLI to convert IP addresses to integer and vice versa";
+    license = licenses.mit;
+    platforms = platforms.linux;
+    maintainers = [ ];
+  };
+}
diff --git a/tools/ipconverter/go.mod b/tools/ipconverter/go.mod
new file mode 100644
index 0000000..49b8653
--- /dev/null
+++ b/tools/ipconverter/go.mod
@@ -0,0 +1,3 @@
+module golang.fcuny.net/ipconverter
+
+go 1.17
diff --git a/tools/ipconverter/main.go b/tools/ipconverter/main.go
new file mode 100644
index 0000000..1211970
--- /dev/null
+++ b/tools/ipconverter/main.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+	"encoding/binary"
+	"fmt"
+	"math/big"
+	"net"
+	"os"
+	"path"
+	"strconv"
+)
+
+func main() {
+	cmd := path.Base(os.Args[1])
+
+	switch cmd {
+	case "ip2int":
+		for _, ip := range os.Args[2:] {
+			r, err := IPtoInt(ip)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "failed to parse %s to an int: %s", ip, err)
+				continue
+			}
+			fmt.Printf("%s\t%d\n", ip, r)
+		}
+	case "int2ip":
+		for _, ip := range os.Args[2:] {
+			r, err := IPtoN(ip)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "failed to parse %s to an addresse IP: %s", ip, err)
+				continue
+			}
+			fmt.Printf("%s\t%s", ip, r)
+		}
+	default:
+		fmt.Printf("`%s' is not a supported command", cmd)
+		os.Exit(1)
+	}
+}
+
+func IPtoInt(ip string) (*big.Int, error) {
+	i := net.ParseIP(ip)
+	if len(i.To4()) == 4 {
+		return big.NewInt(0).SetBytes(i.To4()), nil
+	} else {
+		return nil, fmt.Errorf("%s is not an IPv4 address", ip)
+	}
+}
+
+func IPtoN(ip string) (string, error) {
+	r, err := strconv.Atoi(ip)
+	if err != nil {
+		return "", fmt.Errorf("failed to parse %s to an int: %s", ip, err)
+	}
+	newIP := make(net.IP, 4)
+	binary.BigEndian.PutUint32(newIP, uint32(r))
+	return newIP.To4().String(), nil
+}
diff --git a/tools/ipconverter/main_test.go b/tools/ipconverter/main_test.go
new file mode 100644
index 0000000..394dc28
--- /dev/null
+++ b/tools/ipconverter/main_test.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+	"math/big"
+	"testing"
+)
+
+func TestIPtoInt(t *testing.T) {
+
+	tests := map[string]*big.Int{
+		"192.168.0.1": big.NewInt(3232235521),
+		"10.0.0.1":    big.NewInt(167772161),
+	}
+	for test := range tests {
+		r, err := IPtoInt(test)
+		if err != nil {
+			t.Errorf("failed to convert %s to an int: %s", test, err)
+		}
+		if r.Cmp(tests[test]) != 0 {
+			t.Errorf("convert %s to int, got %d expected %d", test, r, tests[test])
+		}
+	}
+
+	if _, err := IPtoInt("10"); err == nil {
+		t.Error("calling IPtoInt with invalid IP did not result in error")
+	}
+}
+
+
+func TestIPtoN(t *testing.T) {
+	tests := map[string]string{
+		"3232235521": "192.168.0.1",
+		"167772161": "10.0.0.1",
+	}
+	for test := range tests {
+		r, err := IPtoN(test)
+		if err != nil {
+			t.Errorf("failed to convert %s to an address: %s", test, err)
+		}
+		if r != tests[test] {
+			t.Errorf("convert %s to address, got %s expected %s", test, r, tests[test])
+		}
+	}
+}