filter servers using ipv6

This commit is contained in:
Muhammed Efe Cetin
2026-01-06 02:10:19 +01:00
parent 2561e3b56f
commit 0edff1557b
5 changed files with 77 additions and 2 deletions

View File

@@ -232,6 +232,53 @@ func (t *TLSCheck) Check(server *Server, logFields log.Fields) (bool, error) {
return true, nil
}
// IPv6Check verifies that a server has a valid IPv6 address by checking for AAAA records.
type IPv6Check struct {
config *Config
}
// Check verifies IPv6 support for the server by checking for AAAA records
func (i *IPv6Check) Check(server *Server, logFields log.Fields) (bool, error) {
// Extract host from server (handle host:port format)
host := server.Host
if strings.Contains(host, ":") {
var err error
host, _, err = net.SplitHostPort(server.Host)
if err != nil {
host = server.Host
}
}
ips, err := net.LookupIP(host)
if err != nil {
logFields["error"] = err
return true, nil // DNS lookup failure shouldn't fail the whole check
}
// Check if any resolved IP is IPv6
hasIPv6 := false
for _, ip := range ips {
if ip.To4() == nil && ip.To16() != nil {
hasIPv6 = true
break
}
}
server.mu.Lock()
server.IPv6 = hasIPv6
server.mu.Unlock()
if hasIPv6 {
log.WithField("host", server.Host).Debug("Server has IPv6 support")
} else {
logFields["cause"] = "No AAAA record found"
log.WithField("host", server.Host).Debug("Server does not have IPv6 support")
}
// This check doesn't fail servers, it just updates their IPv6 status
return true, nil
}
type VersionCheck struct {
config *Config
VersionURL string

View File

@@ -337,6 +337,17 @@ func (r *Redirector) addServer(server ServerConfig, u *url.URL) (*Server, error)
}).Warning("Could not resolve address")
return nil, err
}
// Check for IPv6 support using resolved IPs
hasIPv6 := false
for _, ip := range ips {
if ip.To4() == nil && ip.To16() != nil {
hasIPv6 = true
break
}
}
s.IPv6 = hasIPv6
var city db.City
err = r.db.Lookup(ips[0], &city)
if err != nil {

View File

@@ -96,9 +96,12 @@ func (r *Redirector) redirectHandler(w http.ResponseWriter, req *http.Request) {
scheme = "http"
}
// Detect if user is connecting via IPv6
isIPv6 := ip.To4() == nil && ip.To16() != nil
// If none of the above exceptions are matched, we use the geographical distance based on IP
if server == nil {
server, distance, err = r.servers.Closest(r, scheme, ip)
server, distance, err = r.servers.Closest(r, scheme, ip, isIPv6)
if err != nil {
log.WithError(err).Warning("Unable to find closest server")

View File

@@ -77,6 +77,9 @@ func New(config *Config) *Redirector {
&TLSCheck{
config: config,
},
&IPv6Check{
config: config,
},
}
if config.CheckURL != "" {

View File

@@ -31,6 +31,7 @@ type Server struct {
Continent string `json:"continent"`
Country string `json:"country"`
Protocols []string `json:"protocols"`
IPv6 bool `json:"ipv6"`
Rules []Rule `json:"rules,omitempty"`
Redirects prometheus.Counter `json:"-"`
LastChange time.Time `json:"lastChange"`
@@ -201,8 +202,12 @@ type ComputedDistance struct {
// it computes the distances. If the nearest server is within a threshold (e.g. 50km),
// it is selected deterministically; otherwise, a weighted selection is used.
// If no local servers exist, it falls back to a weighted selection among all valid servers.
func (s ServerList) Closest(r *Redirector, scheme string, ip net.IP) (*Server, float64, error) {
// If requireIPv6 is true, servers without IPv6 support are filtered out.
func (s ServerList) Closest(r *Redirector, scheme string, ip net.IP, requireIPv6 bool) (*Server, float64, error) {
cacheKey := scheme + "_" + ip.String()
if requireIPv6 {
cacheKey += "_v6"
}
if cached, exists := r.serverCache.Get(cacheKey); exists {
if comp, ok := cached.(ComputedDistance); ok {
@@ -237,6 +242,12 @@ func (s ServerList) Closest(r *Redirector, scheme string, ip net.IP) (*Server, f
if !server.Available || !lo.Contains(server.Protocols, scheme) {
return false
}
// If user is on IPv6, filter out servers that don't support IPv6
if requireIPv6 && !server.IPv6 {
log.WithField("host", server.Host).Debug("Skipping server due to no IPv6 support")
return false
}
if len(server.Rules) > 0 && !server.checkRules(ruleInput) {
log.WithField("host", server.Host).Debug("Skipping server due to rules")
return false