feat: Record node ip addresses #84

For future use investigations about "suspicious" nodes. #105
This commit is contained in:
Christian Ditaputratama 2024-09-12 01:13:30 +07:00
parent 0e3dc04af8
commit f6b048b017
No known key found for this signature in database
GPG key ID: 31D3D06D77950979
6 changed files with 69 additions and 4 deletions

View file

@ -338,6 +338,7 @@ func (p *proberClient) reportResult(node monero.Node, tookTime float64) error {
if !node.IsTor { if !node.IsTor {
if hostIps, err := net.LookupIP(node.Hostname); err == nil { if hostIps, err := net.LookupIP(node.Hostname); err == nil {
node.IPv6Only = ip.IsIPv6Only(hostIps) node.IPv6Only = ip.IsIPv6Only(hostIps)
node.IPAddresses = ip.SliceToString(hostIps)
} }
} }

View file

@ -261,11 +261,14 @@ func v3(db *DB) error {
slog.Debug("[DB] Migrating database schema version 3") slog.Debug("[DB] Migrating database schema version 3")
// table: tbl_node // 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(` _, err := db.Exec(`
ALTER TABLE tbl_node ALTER TABLE tbl_node
ADD ipv6_only TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' ADD COLUMN IF NOT EXISTS ipv6_only TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER cors_capable,
AFTER cors_capable;`) ADD COLUMN ip_addresses TEXT NOT NULL DEFAULT '' AFTER cors_capable;`)
if err != nil { if err != nil {
return err return err
} }

View file

@ -3,6 +3,7 @@ package ip
import ( import (
"net" "net"
"strings"
) )
// IsIPv6Only returns true if all given IPs are IPv6 // IsIPv6Only returns true if all given IPs are IPv6
@ -14,3 +15,14 @@ func IsIPv6Only(ips []net.IP) bool {
} }
return true 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, ",")
}

View file

@ -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)
}
})
}
}

View file

@ -55,6 +55,7 @@ type Node struct {
LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"` LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"`
CORSCapable bool `json:"cors" db:"cors_capable"` CORSCapable bool `json:"cors" db:"cors_capable"`
IPv6Only bool `json:"ipv6_only" db:"ipv6_only"` IPv6Only bool `json:"ipv6_only" db:"ipv6_only"`
IPAddresses string `json:"ip_addresses" db:"ip_addresses"`
} }
// Get node from database by id // Get node from database by id
@ -198,6 +199,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
} }
ipAddr := "" ipAddr := ""
ips := ""
ipv6_only := false ipv6_only := false
if !is_tor { if !is_tor {
@ -217,6 +219,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
} }
ipAddr = hostIp.String() ipAddr = hostIp.String()
ips = ip.SliceToString(hostIps)
} }
row, err := r.db.Query(` row, err := r.db.Query(`
@ -251,6 +254,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
date_entered, date_entered,
last_checked, last_checked,
last_check_status, last_check_status,
ip_addresses,
ipv6_only ipv6_only
) VALUES ( ) VALUES (
?, ?,
@ -264,6 +268,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
?, ?,
?, ?,
?, ?,
?,
? ?
)`, )`,
protocol, protocol,
@ -277,6 +282,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
time.Now().Unix(), time.Now().Unix(),
0, 0,
string(statusDb), string(statusDb),
ips,
ipv6_only) ipv6_only)
if err != nil { if err != nil {
return err return err

View file

@ -309,6 +309,7 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
last_checked = ?, last_checked = ?,
last_check_status = ?, last_check_status = ?,
cors_capable = ?, cors_capable = ?,
ip_addresses = ?,
ipv6_only = ? ipv6_only = ?
WHERE WHERE
id = ?` id = ?`
@ -331,6 +332,7 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
now.Unix(), now.Unix(),
statuses, statuses,
report.Node.CORSCapable, report.Node.CORSCapable,
report.Node.IPAddresses,
report.Node.IPv6Only, report.Node.IPv6Only,
report.Node.ID) report.Node.ID)
if err != nil { if err != nil {
@ -344,10 +346,11 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
uptime = ?, uptime = ?,
last_checked = ?, last_checked = ?,
last_check_status = ?, last_check_status = ?,
ip_addresses = ?,
ipv6_only = ? ipv6_only = ?
WHERE WHERE
id = ?` 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()) slog.Warn(err.Error())
} }
} }