From 13fdf464aec1648c4259e5e7410a389dbef00076 Mon Sep 17 00:00:00 2001 From: liangxu Date: Wed, 8 Jul 2026 16:17:20 +0800 Subject: [PATCH] feat(netutil): add local and private host detection helper Co-Authored-By: Claude Opus 4.8 (1M context) --- .../infrastructure/netutil/local_network.go | 32 ++++++++++++++++++ .../netutil/local_network_test.go | 33 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 server/internal/infrastructure/netutil/local_network.go create mode 100644 server/internal/infrastructure/netutil/local_network_test.go diff --git a/server/internal/infrastructure/netutil/local_network.go b/server/internal/infrastructure/netutil/local_network.go new file mode 100644 index 0000000..3316576 --- /dev/null +++ b/server/internal/infrastructure/netutil/local_network.go @@ -0,0 +1,32 @@ +package netutil + +import ( + "net" + "strings" +) + +func IsLocalOrPrivateHost(host string) bool { + host = strings.ToLower(strings.TrimSpace(host)) + if host == "" { + return false + } + if host == "localhost" || strings.HasSuffix(host, ".localhost") || host == "host.docker.internal" { + return true + } + ip := net.ParseIP(host) + return ip != nil && IsLocalOrPrivateIP(ip) +} + +func IsLocalOrPrivateIP(ip net.IP) bool { + if ip == nil { + return false + } + if ip.IsLoopback() || ip.IsPrivate() || ip.IsUnspecified() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { + return true + } + ip4 := ip.To4() + if ip4 == nil { + return false + } + return ip4[0] == 100 && ip4[1] >= 64 && ip4[1] <= 127 +} diff --git a/server/internal/infrastructure/netutil/local_network_test.go b/server/internal/infrastructure/netutil/local_network_test.go new file mode 100644 index 0000000..597c713 --- /dev/null +++ b/server/internal/infrastructure/netutil/local_network_test.go @@ -0,0 +1,33 @@ +package netutil + +import "testing" + +func TestIsLocalOrPrivateHost(t *testing.T) { + tests := []struct { + name string + host string + want bool + }{ + {name: "localhost", host: "localhost", want: true}, + {name: "localhost subdomain", host: "assets.localhost", want: true}, + {name: "docker host", host: "host.docker.internal", want: true}, + {name: "loopback ipv4", host: "127.0.0.1", want: true}, + {name: "loopback ipv6", host: "::1", want: true}, + {name: "private 10", host: "10.0.0.5", want: true}, + {name: "private 172", host: "172.16.2.3", want: true}, + {name: "private 192", host: "192.168.1.10", want: true}, + {name: "link local", host: "169.254.10.20", want: true}, + {name: "cgnat", host: "100.64.10.20", want: true}, + {name: "unspecified", host: "0.0.0.0", want: true}, + {name: "public ip", host: "8.8.8.8", want: false}, + {name: "public hostname", host: "example.com", want: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsLocalOrPrivateHost(tt.host); got != tt.want { + t.Fatalf("IsLocalOrPrivateHost(%q) = %t, want %t", tt.host, got, tt.want) + } + }) + } +}