Skip to content

Commit 0edff15

Browse files
committed
filter servers using ipv6
1 parent 2561e3b commit 0edff15

5 files changed

Lines changed: 77 additions & 2 deletions

File tree

check.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,53 @@ func (t *TLSCheck) Check(server *Server, logFields log.Fields) (bool, error) {
232232
return true, nil
233233
}
234234

235+
// IPv6Check verifies that a server has a valid IPv6 address by checking for AAAA records.
236+
type IPv6Check struct {
237+
config *Config
238+
}
239+
240+
// Check verifies IPv6 support for the server by checking for AAAA records
241+
func (i *IPv6Check) Check(server *Server, logFields log.Fields) (bool, error) {
242+
// Extract host from server (handle host:port format)
243+
host := server.Host
244+
if strings.Contains(host, ":") {
245+
var err error
246+
host, _, err = net.SplitHostPort(server.Host)
247+
if err != nil {
248+
host = server.Host
249+
}
250+
}
251+
252+
ips, err := net.LookupIP(host)
253+
if err != nil {
254+
logFields["error"] = err
255+
return true, nil // DNS lookup failure shouldn't fail the whole check
256+
}
257+
258+
// Check if any resolved IP is IPv6
259+
hasIPv6 := false
260+
for _, ip := range ips {
261+
if ip.To4() == nil && ip.To16() != nil {
262+
hasIPv6 = true
263+
break
264+
}
265+
}
266+
267+
server.mu.Lock()
268+
server.IPv6 = hasIPv6
269+
server.mu.Unlock()
270+
271+
if hasIPv6 {
272+
log.WithField("host", server.Host).Debug("Server has IPv6 support")
273+
} else {
274+
logFields["cause"] = "No AAAA record found"
275+
log.WithField("host", server.Host).Debug("Server does not have IPv6 support")
276+
}
277+
278+
// This check doesn't fail servers, it just updates their IPv6 status
279+
return true, nil
280+
}
281+
235282
type VersionCheck struct {
236283
config *Config
237284
VersionURL string

config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,17 @@ func (r *Redirector) addServer(server ServerConfig, u *url.URL) (*Server, error)
337337
}).Warning("Could not resolve address")
338338
return nil, err
339339
}
340+
341+
// Check for IPv6 support using resolved IPs
342+
hasIPv6 := false
343+
for _, ip := range ips {
344+
if ip.To4() == nil && ip.To16() != nil {
345+
hasIPv6 = true
346+
break
347+
}
348+
}
349+
s.IPv6 = hasIPv6
350+
340351
var city db.City
341352
err = r.db.Lookup(ips[0], &city)
342353
if err != nil {

http.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,12 @@ func (r *Redirector) redirectHandler(w http.ResponseWriter, req *http.Request) {
9696
scheme = "http"
9797
}
9898

99+
// Detect if user is connecting via IPv6
100+
isIPv6 := ip.To4() == nil && ip.To16() != nil
101+
99102
// If none of the above exceptions are matched, we use the geographical distance based on IP
100103
if server == nil {
101-
server, distance, err = r.servers.Closest(r, scheme, ip)
104+
server, distance, err = r.servers.Closest(r, scheme, ip, isIPv6)
102105

103106
if err != nil {
104107
log.WithError(err).Warning("Unable to find closest server")

redirector.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ func New(config *Config) *Redirector {
7777
&TLSCheck{
7878
config: config,
7979
},
80+
&IPv6Check{
81+
config: config,
82+
},
8083
}
8184

8285
if config.CheckURL != "" {

servers.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Server struct {
3131
Continent string `json:"continent"`
3232
Country string `json:"country"`
3333
Protocols []string `json:"protocols"`
34+
IPv6 bool `json:"ipv6"`
3435
Rules []Rule `json:"rules,omitempty"`
3536
Redirects prometheus.Counter `json:"-"`
3637
LastChange time.Time `json:"lastChange"`
@@ -201,8 +202,12 @@ type ComputedDistance struct {
201202
// it computes the distances. If the nearest server is within a threshold (e.g. 50km),
202203
// it is selected deterministically; otherwise, a weighted selection is used.
203204
// If no local servers exist, it falls back to a weighted selection among all valid servers.
204-
func (s ServerList) Closest(r *Redirector, scheme string, ip net.IP) (*Server, float64, error) {
205+
// If requireIPv6 is true, servers without IPv6 support are filtered out.
206+
func (s ServerList) Closest(r *Redirector, scheme string, ip net.IP, requireIPv6 bool) (*Server, float64, error) {
205207
cacheKey := scheme + "_" + ip.String()
208+
if requireIPv6 {
209+
cacheKey += "_v6"
210+
}
206211

207212
if cached, exists := r.serverCache.Get(cacheKey); exists {
208213
if comp, ok := cached.(ComputedDistance); ok {
@@ -237,6 +242,12 @@ func (s ServerList) Closest(r *Redirector, scheme string, ip net.IP) (*Server, f
237242
if !server.Available || !lo.Contains(server.Protocols, scheme) {
238243
return false
239244
}
245+
246+
// If user is on IPv6, filter out servers that don't support IPv6
247+
if requireIPv6 && !server.IPv6 {
248+
log.WithField("host", server.Host).Debug("Skipping server due to no IPv6 support")
249+
return false
250+
}
240251
if len(server.Rules) > 0 && !server.checkRules(ruleInput) {
241252
log.WithField("host", server.Host).Debug("Skipping server due to rules")
242253
return false

0 commit comments

Comments
 (0)