From f6b048b017586fce75fee342ba0459c68bf4b4cd Mon Sep 17 00:00:00 2001 From: Christian Ditaputratama Date: Thu, 12 Sep 2024 01:13:30 +0700 Subject: [PATCH] feat: Record node ip addresses #84 For future use investigations about "suspicious" nodes. #105 --- cmd/client/probe.go | 1 + internal/database/schema.go | 9 ++++++--- internal/ip/ip.go | 12 +++++++++++ internal/ip/ip_test.go | 40 +++++++++++++++++++++++++++++++++++++ internal/monero/monero.go | 6 ++++++ internal/monero/report.go | 5 ++++- 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/cmd/client/probe.go b/cmd/client/probe.go index 7dc3357..9fabb09 100644 --- a/cmd/client/probe.go +++ b/cmd/client/probe.go @@ -338,6 +338,7 @@ func (p *proberClient) reportResult(node monero.Node, tookTime float64) error { if !node.IsTor { if hostIps, err := net.LookupIP(node.Hostname); err == nil { node.IPv6Only = ip.IsIPv6Only(hostIps) + node.IPAddresses = ip.SliceToString(hostIps) } } diff --git a/internal/database/schema.go b/internal/database/schema.go index a6bec7d..a6c39a3 100644 --- a/internal/database/schema.go +++ b/internal/database/schema.go @@ -261,11 +261,14 @@ func v3(db *DB) error { slog.Debug("[DB] Migrating database schema version 3") // table: tbl_node - slog.Debug("[DB] Adding ipv6_only column to tbl_node") + // TODO: Remove IF NOT EXISTS SQL statement below after merging to main + // branch. The statement only to accomodate commit 518d4b4 so future main + // branch keep on schema version 3. + slog.Debug("[DB] Adding additional columns to tbl_node") _, err := db.Exec(` ALTER TABLE tbl_node - ADD ipv6_only TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' - AFTER cors_capable;`) + ADD COLUMN IF NOT EXISTS ipv6_only TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER cors_capable, + ADD COLUMN ip_addresses TEXT NOT NULL DEFAULT '' AFTER cors_capable;`) if err != nil { return err } diff --git a/internal/ip/ip.go b/internal/ip/ip.go index 410ba34..493d234 100644 --- a/internal/ip/ip.go +++ b/internal/ip/ip.go @@ -3,6 +3,7 @@ package ip import ( "net" + "strings" ) // IsIPv6Only returns true if all given IPs are IPv6 @@ -14,3 +15,14 @@ func IsIPv6Only(ips []net.IP) bool { } return true } + +// SliceToString converts []net.IP to a string separated by comma. +// If the separator is empty, it defaults to ",". +func SliceToString(ips []net.IP) string { + r := make([]string, len(ips)) + for i, j := range ips { + r[i] = j.String() + } + + return strings.Join(r, ",") +} diff --git a/internal/ip/ip_test.go b/internal/ip/ip_test.go index ae6730a..28012b2 100644 --- a/internal/ip/ip_test.go +++ b/internal/ip/ip_test.go @@ -44,3 +44,43 @@ func TestIsIPv6Only(t *testing.T) { }) } } + +// Single test: go test ./internal/ip -bench TestSliceToString -benchmem -run=^$ -v +func TestSliceToString(t *testing.T) { + tests := []struct { + name string + ips []net.IP + want string + }{ + { + name: "IPv4", + ips: []net.IP{ + net.ParseIP("1.1.1.1"), + }, + want: "1.1.1.1", + }, + { + name: "IPv6", + ips: []net.IP{ + net.ParseIP("2606:4700::6810:85e5"), + }, + want: "2606:4700::6810:85e5", + }, + { + name: "IPv6 and IPv4", + ips: []net.IP{ + net.ParseIP("1.1.1.1"), + net.ParseIP("2606:4700::6810:85e5"), + }, + want: "1.1.1.1,2606:4700::6810:85e5", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := SliceToString(tt.ips); got != tt.want { + t.Errorf("SliceToString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/monero/monero.go b/internal/monero/monero.go index 85150dc..750265c 100644 --- a/internal/monero/monero.go +++ b/internal/monero/monero.go @@ -55,6 +55,7 @@ type Node struct { LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"` CORSCapable bool `json:"cors" db:"cors_capable"` IPv6Only bool `json:"ipv6_only" db:"ipv6_only"` + IPAddresses string `json:"ip_addresses" db:"ip_addresses"` } // Get node from database by id @@ -198,6 +199,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error { } ipAddr := "" + ips := "" ipv6_only := false if !is_tor { @@ -217,6 +219,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error { } ipAddr = hostIp.String() + ips = ip.SliceToString(hostIps) } row, err := r.db.Query(` @@ -251,6 +254,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error { date_entered, last_checked, last_check_status, + ip_addresses, ipv6_only ) VALUES ( ?, @@ -264,6 +268,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error { ?, ?, ?, + ?, ? )`, protocol, @@ -277,6 +282,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error { time.Now().Unix(), 0, string(statusDb), + ips, ipv6_only) if err != nil { return err diff --git a/internal/monero/report.go b/internal/monero/report.go index 1aaa834..232cc5e 100644 --- a/internal/monero/report.go +++ b/internal/monero/report.go @@ -309,6 +309,7 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error { last_checked = ?, last_check_status = ?, cors_capable = ?, + ip_addresses = ?, ipv6_only = ? WHERE id = ?` @@ -331,6 +332,7 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error { now.Unix(), statuses, report.Node.CORSCapable, + report.Node.IPAddresses, report.Node.IPv6Only, report.Node.ID) if err != nil { @@ -344,10 +346,11 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error { uptime = ?, last_checked = ?, last_check_status = ?, + ip_addresses = ?, ipv6_only = ? WHERE id = ?` - if _, err := r.db.Exec(u, 0, report.Node.Uptime, now.Unix(), statuses, report.Node.IPv6Only, report.Node.ID); err != nil { + if _, err := r.db.Exec(u, 0, report.Node.Uptime, now.Unix(), statuses, report.Node.IPAddresses, report.Node.IPv6Only, report.Node.ID); err != nil { slog.Warn(err.Error()) } }