feat: Check IP-stack info everytime prober send report #84

This commit add IsIPv6Only function inside `internal/ip` package
and moving `geo` package from `internal/geo` to `internal/ip/geo`.

Although it increases server resource usage, checking hostname to IP is
required every time the prober sends a report so that the `ipv6_only`
record in the database is not up-to-date. Previously, this feature did
not exist.
This commit is contained in:
Christian Ditaputratama 2024-09-09 18:21:03 +07:00
parent 518d4b4335
commit c3f837e122
No known key found for this signature in database
GPG key ID: 31D3D06D77950979
7 changed files with 84 additions and 17 deletions

View file

@ -38,14 +38,14 @@ To build the executable binaries, you need:
- MySQL/MariaDB
- [GeoIP Database][geoip_doc] (optional). Place it to `./assets/geoip`,
see [./internal/geo/ip.go](./internal/geo/ip.go).
see [./internal/ip/geo/geoip.go](./internal/ip/geo/geoip.go).
## Installation
### For initial server setup:
1. Download [GeoIP Database][geoip_doc] and place it to `./assets/geoip`.
(see [./internal/geo/ip.go](./internal/geo/ip.go)).
(see [./internal/ip/geo/geoip.go](./internal/ip/geo/geoip.go)).
2. Pepare your MySQL/MariaDB.
3. Copy `.env.example` to `.env` and edit it to match with server environment.
4. Build the binary with `make server` (or `make build` to build both

View file

@ -14,6 +14,7 @@ import (
"time"
"github.com/ditatompel/xmr-remote-nodes/internal/config"
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
"github.com/spf13/cobra"
@ -334,6 +335,12 @@ func (p *proberClient) fetchFee(client http.Client, endpoint string) (uint, erro
}
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)
}
}
jsonData, err := json.Marshal(monero.ProbeReport{
TookTime: tookTime,
Message: p.message,

16
internal/ip/ip.go Normal file
View file

@ -0,0 +1,16 @@
// Package ip provides IP address related functions
package ip
import (
"net"
)
// IsIPv6Only returns true if all given IPs are IPv6
func IsIPv6Only(ips []net.IP) bool {
for _, ip := range ips {
if ip.To4() != nil {
return false
}
}
return true
}

46
internal/ip/ip_test.go Normal file
View file

@ -0,0 +1,46 @@
package ip
import (
"net"
"testing"
)
// Single test: go test ./internal/ip -bench TestIsIPv6Only -benchmem -run=^$ -v
func TestIsIPv6Only(t *testing.T) {
tests := []struct {
name string
ips []net.IP
want bool
}{
{
name: "IPv4",
ips: []net.IP{
net.ParseIP("1.1.1.1"),
},
want: false,
},
{
name: "IPv6",
ips: []net.IP{
net.ParseIP("2606:4700::6810:85e5"),
},
want: true,
},
{
name: "IPv6 and IPv4",
ips: []net.IP{
net.ParseIP("1.1.1.1"),
net.ParseIP("2606:4700::6810:84e5"),
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsIPv6Only(tt.ips); got != tt.want {
t.Errorf("IsIPv6Only() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -12,7 +12,7 @@ import (
"time"
"github.com/ditatompel/xmr-remote-nodes/internal/database"
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
"github.com/jmoiron/sqlx/types"
)
@ -196,9 +196,9 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
if strings.HasSuffix(hostname, ".onion") {
is_tor = true
}
ip := ""
ipv6_only := true
ipAddr := ""
ipv6_only := false
if !is_tor {
hostIps, err := net.LookupIP(hostname)
@ -206,12 +206,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
return err
}
for _, hostIp := range hostIps {
if hostIp.To4() != nil {
ipv6_only = false
break
}
}
ipv6_only = ip.IsIPv6Only(hostIps)
hostIp := hostIps[0]
if hostIp.IsPrivate() {
@ -221,7 +216,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
return errors.New("IP address is loopback address")
}
ip = hostIp.String()
ipAddr = hostIp.String()
}
row, err := r.db.Query(`
@ -276,7 +271,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
port,
is_tor,
"",
ip,
ipAddr,
0,
0,
time.Now().Unix(),

View file

@ -10,7 +10,7 @@ import (
"strings"
"time"
"github.com/ditatompel/xmr-remote-nodes/internal/geo"
"github.com/ditatompel/xmr-remote-nodes/internal/ip/geo"
)
type QueryLogs struct {
@ -308,7 +308,8 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
city = ?,
last_checked = ?,
last_check_status = ?,
cors_capable = ?
cors_capable = ?,
ipv6_only = ?
WHERE
id = ?`
_, err := r.db.Exec(update,
@ -330,6 +331,7 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
now.Unix(),
statuses,
report.Node.CORSCapable,
report.Node.IPv6Only,
report.Node.ID)
if err != nil {
slog.Warn(err.Error())
@ -341,10 +343,11 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
is_available = ?,
uptime = ?,
last_checked = ?,
last_check_status = ?
last_check_status = ?,
ipv6_only = ?
WHERE
id = ?`
if _, err := r.db.Exec(u, 0, report.Node.Uptime, now.Unix(), statuses, report.Node.ID); err != nil {
if _, err := r.db.Exec(u, 0, report.Node.Uptime, now.Unix(), statuses, report.Node.IPv6Only, report.Node.ID); err != nil {
slog.Warn(err.Error())
}
}