Skip to content

Commit 1799abc

Browse files
committed
lib: fix IP selection in DNS caching
Fixes #677
1 parent b0b14b9 commit 1799abc

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

lib/attack.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,33 @@ func DNSCaching(ttl time.Duration) func(*Attacker) {
375375
}
376376
}
377377

378+
// firstOfEachIPFamily returns the first IP of each IP family in the input slice.
379+
func firstOfEachIPFamily(ips []string) []string {
380+
if len(ips) == 0 {
381+
return ips
382+
}
383+
384+
var (
385+
lastV4 bool
386+
each = ips[:0]
387+
)
388+
389+
for i := 0; i < len(ips) && len(each) < 2; i++ {
390+
ip := net.ParseIP(ips[i])
391+
if ip == nil {
392+
continue
393+
}
394+
395+
isV4 := ip.To4() != nil
396+
if len(each) == 0 || isV4 != lastV4 {
397+
each = append(each, ips[i])
398+
lastV4 = isV4
399+
}
400+
}
401+
402+
return each
403+
}
404+
378405
type attack struct {
379406
name string
380407
began time.Time

lib/attack_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717
"strings"
1818
"testing"
1919
"time"
20+
21+
"github.com/google/go-cmp/cmp"
2022
)
2123

2224
func TestAttackRate(t *testing.T) {
@@ -420,3 +422,79 @@ func TestDNSCaching_Issue649(t *testing.T) {
420422
atk := NewAttacker(DNSCaching(0))
421423
_ = atk.hit(tr, &attack{name: "TEST", began: time.Now()})
422424
}
425+
426+
func TestFirstOfEachIPFamily(t *testing.T) {
427+
tests := []struct {
428+
name string
429+
input []string
430+
want []string
431+
}{
432+
{
433+
name: "empty list",
434+
input: []string{},
435+
want: []string{},
436+
},
437+
{
438+
name: "single IPv4",
439+
input: []string{"192.168.1.1"},
440+
want: []string{"192.168.1.1"},
441+
},
442+
{
443+
name: "single IPv6",
444+
input: []string{"fe80::1"},
445+
want: []string{"fe80::1"},
446+
},
447+
{
448+
name: "multiple IPv6",
449+
input: []string{"fe80::1", "fe80::2"},
450+
want: []string{"fe80::1"},
451+
},
452+
{
453+
name: "one IPv4 and one IPv6",
454+
input: []string{"192.168.1.1", "fe80::1"},
455+
want: []string{"192.168.1.1", "fe80::1"},
456+
},
457+
{
458+
name: "one IPv6 and one IPv4",
459+
input: []string{"fe80::1", "192.168.1.1"},
460+
want: []string{"fe80::1", "192.168.1.1"},
461+
},
462+
{
463+
name: "multiple IPs with alternating versions",
464+
input: []string{"192.168.1.1", "fe80::1", "192.168.1.2", "fe80::2"},
465+
want: []string{"192.168.1.1", "fe80::1"},
466+
},
467+
{
468+
name: "multiple IPs with same versions",
469+
input: []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"},
470+
want: []string{"192.168.1.1"},
471+
},
472+
{
473+
name: "multiple IPs with non-alternating versions",
474+
input: []string{"192.168.1.1", "fe80::1", "192.168.1.2", "192.168.1.3", "fe80::2"},
475+
want: []string{"192.168.1.1", "fe80::1"},
476+
},
477+
{
478+
name: "invalid IP addresses",
479+
input: []string{"invalid", "192.168.1.1", "fe80::1"},
480+
want: []string{"192.168.1.1", "fe80::1"},
481+
},
482+
{
483+
name: "IPv4 with embedded IPv6",
484+
input: []string{"192.168.1.1", "::ffff:c000:280", "fe80::1"},
485+
want: []string{"192.168.1.1", "fe80::1"},
486+
},
487+
}
488+
489+
for _, tt := range tests {
490+
t.Run(tt.name, func(t *testing.T) {
491+
result := firstOfEachIPFamily(tt.input)
492+
if len(result) != len(tt.want) {
493+
t.Fatalf("want %v, got %v", tt.want, result)
494+
}
495+
if diff := cmp.Diff(tt.want, result); diff != "" {
496+
t.Errorf("unexpected result (-want +got):\n%s", diff)
497+
}
498+
})
499+
}
500+
}

0 commit comments

Comments
 (0)