From ca9b2e483137fe9f58fb6562a1a7ea5acab311da Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 14 Apr 2022 04:36:15 +0300 Subject: [PATCH 1/5] [iprange] Fuzzing task, first commit. --- iprange/README.md | 33 +++ iprange/example_test.go | 25 ++ iprange/funcs.go | 94 +++++++ iprange/iprange_test.go | 172 +++++++++++++ iprange/lex.go | 86 +++++++ iprange/sortip.go | 29 +++ iprange/y.go | 540 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 979 insertions(+) create mode 100644 iprange/README.md create mode 100644 iprange/example_test.go create mode 100644 iprange/funcs.go create mode 100644 iprange/iprange_test.go create mode 100644 iprange/lex.go create mode 100644 iprange/sortip.go create mode 100644 iprange/y.go diff --git a/iprange/README.md b/iprange/README.md new file mode 100644 index 0000000..933254a --- /dev/null +++ b/iprange/README.md @@ -0,0 +1,33 @@ +## iprange [BONUS] + +В этой задаче вам предстоит познакомиться с фаззингом, и его нативной поддержкой в go. + +Нужно поправить баги в функции `ParseList`. + +`ParseList` принимает на вход строку с описанием рейнджей ip адрессов в одном из `n` форматов +* `10.0.0.1` +* `10.0.0.0/24` +* `10.0.0.*` +* `10.0.0.1-10` +и возвращает список пар `(min ip, max ip)` (см. [example](./example_test.go)). + +Для обнаружения бага (crash функции) предлагается написать fuzz тест на функцию `ParseList`. + +### Проверка решения + +Во-первых должны работать имеющиеся тесты +``` +go test -v ./iprange... +``` + +Во-вторых, в CI есть приватные тесты, молча падающие на неправильной `ParseList`. + +Как запустить fuzz тесты? +``` +go test -v -fuzz=. ./iprange... +``` + +### Ссылки + +* design draft https://go.googlesource.com/proposal/+/master/design/draft-fuzzing.md +* fuzzing tutorial в блоге go https://go.dev/doc/tutorial/fuzz diff --git a/iprange/example_test.go b/iprange/example_test.go new file mode 100644 index 0000000..8fcd911 --- /dev/null +++ b/iprange/example_test.go @@ -0,0 +1,25 @@ +package iprange_test + +import ( + "fmt" + "log" + + "gitlab.com/slon/shad-go/iprange" +) + +func ExampleParseList() { + list, err := iprange.ParseList("10.0.0.1, 10.0.0.5-10, 192.168.1.*, 192.168.10.0/24") + if err != nil { + log.Fatal(err) + } + + for _, i := range list { + fmt.Println(i) + } + + // Output: + // {10.0.0.1 10.0.0.1} + // {10.0.0.5 10.0.0.10} + // {192.168.1.0 192.168.1.255} + // {192.168.10.0 192.168.10.255} +} diff --git a/iprange/funcs.go b/iprange/funcs.go new file mode 100644 index 0000000..ed99418 --- /dev/null +++ b/iprange/funcs.go @@ -0,0 +1,94 @@ +//go:build !solution + +package iprange + +import ( + "encoding/binary" + "net" + "sort" +) + +func streamRange(lower, upper net.IP) chan net.IP { + ipchan := make(chan net.IP, 1) + + rangeMask := net.IP([]byte{ + upper[0] - lower[0], + upper[1] - lower[1], + upper[2] - lower[2], + upper[3] - lower[3], + }) + + go func() { + defer close(ipchan) + + lower32 := binary.BigEndian.Uint32([]byte(lower)) + upper32 := binary.BigEndian.Uint32([]byte(upper)) + diff := upper32 - lower32 + + if diff < 0 { + panic("Lower address is actually higher than upper address.") + } + + mask := net.IP([]byte{0, 0, 0, 0}) + + for { + ipchan <- net.IP([]byte{ + lower[0] + mask[0], + lower[1] + mask[1], + lower[2] + mask[2], + lower[3] + mask[3], + }) + + if mask.Equal(rangeMask) { + break + } + + for i := 3; i >= 0; i-- { + if rangeMask[i] > 0 { + if mask[i] < rangeMask[i] { + mask[i] = mask[i] + 1 + break + } else { + mask[i] = mask[i] % rangeMask[i] + if i < 1 { + break + } + } + } + } + } + + }() + + return ipchan +} + +// Expand expands an address with a mask taken from a stream +func (r *AddressRange) Expand() []net.IP { + ips := []net.IP{} + for ip := range streamRange(r.Min, r.Max) { + ips = append(ips, ip) + } + return ips +} + +// Expand expands and normalizes a set of parsed target specifications +func (l AddressRangeList) Expand() []net.IP { + var res []net.IP + for i := range l { + res = append(res, l[i].Expand()...) + } + return normalize(res) +} + +func normalize(src []net.IP) []net.IP { + sort.Sort(asc(src)) + dst := make([]net.IP, 1, len(src)) + dst[0] = src[0] + for i := range src { + if !dst[len(dst)-1].Equal(src[i]) { + dst = append(dst, src[i]) + } + } + return dst +} diff --git a/iprange/iprange_test.go b/iprange/iprange_test.go new file mode 100644 index 0000000..8c2d6e7 --- /dev/null +++ b/iprange/iprange_test.go @@ -0,0 +1,172 @@ +package iprange + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleAddress(t *testing.T) { + ipRange, err := Parse("192.168.1.1") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 1).To4(), ipRange.Min) + assert.Equal(t, ipRange.Min, ipRange.Max) +} + +func TestCIDRAddress(t *testing.T) { + { + ipRange, err := Parse("192.168.1.1/24") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 1, 255).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("192.168.2.1/24") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 2, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 2, 255).To4(), ipRange.Max) + + out := ipRange.Expand() + assert.Equal(t, int(0xffffffff-0xffffff00), len(out)-1) + for i := 0; i < 256; i++ { + assert.Equal(t, net.IP([]byte{192, 168, 2, byte(i)}), out[i]) + } + } + + { + ipRange, err := Parse("10.1.2.3/16") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(10, 1, 0, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(10, 1, 255, 255).To4(), ipRange.Max) + + out := ipRange.Expand() + assert.Equal(t, int(0xffffffff-0xffff0000), len(out)-1) + for i := 0; i < 65536; i++ { + assert.Equal(t, net.IP([]byte{10, 1, byte(i / 256), byte(i % 256)}), out[i]) + } + } + + { + ipRange, err := Parse("10.1.2.3/32") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(10, 1, 2, 3).To4(), ipRange.Min) + assert.Equal(t, ipRange.Min, ipRange.Max) + } +} + +func TestWildcardAddress(t *testing.T) { + ipRange, err := Parse("192.168.1.*") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 1, 255).To4(), ipRange.Max) +} + +func TestRangeAddress(t *testing.T) { + { + ipRange, err := Parse("192.168.1.10-20") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 10).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 1, 20).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("192.168.10-20.1") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 10, 1).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 20, 1).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("0-255.1.1.1") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(0, 1, 1, 1).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(255, 1, 1, 1).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("1-2.3-4.5-6.7-8") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(1, 3, 5, 7).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(2, 4, 6, 8).To4(), ipRange.Max) + + out := ipRange.Expand() + + assert.Equal(t, 16, len(out)) + assert.Equal(t, out, []net.IP{ + net.IP([]byte{1, 3, 5, 7}), + net.IP([]byte{1, 3, 5, 8}), + net.IP([]byte{1, 3, 6, 7}), + net.IP([]byte{1, 3, 6, 8}), + net.IP([]byte{1, 4, 5, 7}), + net.IP([]byte{1, 4, 5, 8}), + net.IP([]byte{1, 4, 6, 7}), + net.IP([]byte{1, 4, 6, 8}), + net.IP([]byte{2, 3, 5, 7}), + net.IP([]byte{2, 3, 5, 8}), + net.IP([]byte{2, 3, 6, 7}), + net.IP([]byte{2, 3, 6, 8}), + net.IP([]byte{2, 4, 5, 7}), + net.IP([]byte{2, 4, 5, 8}), + net.IP([]byte{2, 4, 6, 7}), + net.IP([]byte{2, 4, 6, 8}), + }) + } +} + +func TestMixedAddress(t *testing.T) { + ipRange, err := Parse("192.168.10-20.*/25") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 10, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 10, 127).To4(), ipRange.Max) +} + +func TestList(t *testing.T) { + rangeList, err := ParseList("192.168.1.1, 192.168.1.1/24, 192.168.1.*, 192.168.1.10-20") + assert.Nil(t, err) + assert.Len(t, rangeList, 4) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 1}), rangeList[0].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 1}), rangeList[0].Max) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 0}), rangeList[1].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 255}), rangeList[1].Max) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 0}), rangeList[2].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 255}), rangeList[2].Max) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 10}), rangeList[3].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 20}), rangeList[3].Max) +} + +func TestBadAddress(t *testing.T) { + ipRange, err := Parse("192.168.10") + assert.Nil(t, ipRange) + assert.Error(t, err) +} + +func TestBadList(t *testing.T) { + rangeList, err := ParseList("192.168.1,, 192.168.1.1/24, 192.168.1.*, 192.168.1.10-20") + assert.Error(t, err) + assert.Nil(t, rangeList) +} + +func TestListExpansion(t *testing.T) { + rangeList, err := ParseList("192.168.1.10, 192.168.1.1-20, 192.168.1.10/29") + assert.Nil(t, err) + + expanded := rangeList.Expand() + assert.Len(t, expanded, 20) +} diff --git a/iprange/lex.go b/iprange/lex.go new file mode 100644 index 0000000..6277511 --- /dev/null +++ b/iprange/lex.go @@ -0,0 +1,86 @@ +//go:build !solution + +package iprange + +import ( + "bytes" + "errors" + "log" + "strconv" + "unicode/utf8" +) + +const eof = 0 + +type ipLex struct { + line []byte + peek rune + output AddressRangeList + err error +} + +func (ip *ipLex) Lex(yylval *ipSymType) int { + for { + c := ip.next() + switch c { + case eof: + return eof + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return ip.byte(c, yylval) + default: + return int(c) + } + } +} + +func (ip *ipLex) byte(c rune, yylval *ipSymType) int { + add := func(b *bytes.Buffer, c rune) { + if _, err := b.WriteRune(c); err != nil { + log.Fatalf("WriteRune: %s", err) + } + } + var b bytes.Buffer + add(&b, c) +L: + for { + c = ip.next() + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + add(&b, c) + default: + break L + } + } + if c != eof { + ip.peek = c + } + octet, err := strconv.ParseUint(b.String(), 10, 32) + if err != nil { + log.Printf("badly formatted octet") + return eof + } + yylval.num = byte(octet) + return num +} + +func (ip *ipLex) next() rune { + if ip.peek != eof { + r := ip.peek + ip.peek = eof + return r + } + if len(ip.line) == 0 { + return eof + } + c, size := utf8.DecodeRune(ip.line) + ip.line = ip.line[size:] + if c == utf8.RuneError && size == 1 { + log.Print("invalid utf8") + return ip.next() + } + return c +} + +func (ip *ipLex) Error(s string) { + ip.err = errors.New(s) +} diff --git a/iprange/sortip.go b/iprange/sortip.go new file mode 100644 index 0000000..234f662 --- /dev/null +++ b/iprange/sortip.go @@ -0,0 +1,29 @@ +//go:build !solution + +package iprange + +import ( + "math/big" + "net" +) + +// Asc implements sorting in ascending order for IP addresses +type asc []net.IP + +func (a asc) Len() int { + return len(a) +} + +func (a asc) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a asc) Less(i, j int) bool { + bigi := big.NewInt(0).SetBytes(a[i]) + bigj := big.NewInt(0).SetBytes(a[j]) + + if bigi.Cmp(bigj) == -1 { + return true + } + return false +} diff --git a/iprange/y.go b/iprange/y.go new file mode 100644 index 0000000..f469c02 --- /dev/null +++ b/iprange/y.go @@ -0,0 +1,540 @@ +//go:build !solution + +package iprange + +import ( + "encoding/binary" + "fmt" + "net" +) + +type AddressRangeList []AddressRange + +type AddressRange struct { + Min net.IP + Max net.IP +} + +type octetRange struct { + min byte + max byte +} + +type ipSymType struct { + yys int + num byte + octRange octetRange + addrRange AddressRange + result AddressRangeList +} + +const num = 57346 + +var ipToknames = [...]string{ + "$end", + "error", + "$unk", + "num", + "','", + "' '", + "'/'", + "'.'", + "'*'", + "'-'", +} +var ipStatenames = [...]string{} + +const ipEofCode = 1 +const ipErrCode = 2 +const ipInitialStackSize = 16 + +// ParseList takes a list of target specifications and returns a list of ranges, +// even if the list contains a single element. +func ParseList(in string) (AddressRangeList, error) { + lex := &ipLex{line: []byte(in)} + errCode := ipParse(lex) + if errCode != 0 || lex.err != nil { + return nil, fmt.Errorf("could not parse target: %w", lex.err) + } + return lex.output, nil +} + +// Parse takes a single target specification and returns a range. It effectively calls ParseList +// and returns the first result +func Parse(in string) (*AddressRange, error) { + l, err := ParseList(in) + if err != nil { + return nil, err + } + return &l[0], nil +} + +var ipExca = [...]int{ + -1, 1, + 1, -1, + -2, 0, +} + +const ipNprod = 12 +const ipPrivate = 57344 + +var ipTokenNames []string +var ipStates []string + +const ipLast = 22 + +var ipAct = [...]int{ + + 4, 5, 12, 20, 2, 10, 6, 18, 11, 14, + 9, 17, 16, 13, 15, 8, 1, 7, 3, 19, + 0, 21, +} +var ipPact = [...]int{ + + -3, 5, -1000, -2, 0, -8, -1000, -1000, -3, 3, + 10, -3, 7, -1000, -1000, -1000, -1, -1000, -3, -5, + -3, -1000, +} +var ipPgo = [...]int{ + + 0, 18, 4, 0, 17, 16, 15, +} +var ipR1 = [...]int{ + + 0, 5, 5, 6, 6, 2, 2, 1, 3, 3, + 3, 4, +} +var ipR2 = [...]int{ + + 0, 1, 3, 1, 2, 3, 1, 7, 1, 1, + 1, 3, +} +var ipChk = [...]int{ + + -1000, -5, -2, -1, -3, 4, 9, -4, -6, 5, + 7, 8, 10, -2, 6, 4, -3, 4, 8, -3, + 8, -3, +} +var ipDef = [...]int{ + + 0, -2, 1, 6, 0, 8, 9, 10, 0, 3, + 0, 0, 0, 2, 4, 5, 0, 11, 0, 0, + 0, 7, +} +var ipTok1 = [...]int{ + + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 6, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 9, 3, 5, 10, 8, 7, +} +var ipTok2 = [...]int{ + + 2, 3, 4, +} +var ipTok3 = [...]int{ + 0, +} + +var ipErrorMessages = [...]struct { + state int + token int + msg string +}{} + +/* parser for yacc output */ + +var ( + ipDebug = 0 + ipErrorVerbose = false +) + +type ipLexer interface { + Lex(lval *ipSymType) int + Error(s string) +} + +type ipParser interface { + Parse(ipLexer) int + Lookahead() int +} + +type ipParserImpl struct { + lval ipSymType + stack [ipInitialStackSize]ipSymType + char int +} + +func (p *ipParserImpl) Lookahead() int { + return p.char +} + +func ipNewParser() ipParser { + return &ipParserImpl{} +} + +const ipFlag = -1000 + +func ipTokname(c int) string { + if c >= 1 && c-1 < len(ipToknames) { + if ipToknames[c-1] != "" { + return ipToknames[c-1] + } + } + return fmt.Sprintf("tok-%v", c) +} + +func ipStatname(s int) string { + if s >= 0 && s < len(ipStatenames) { + if ipStatenames[s] != "" { + return ipStatenames[s] + } + } + return fmt.Sprintf("state-%v", s) +} + +func ipErrorMessage(state, lookAhead int) string { + const TOKSTART = 4 + + if !ipErrorVerbose { + return "syntax error" + } + + for _, e := range ipErrorMessages { + if e.state == state && e.token == lookAhead { + return "syntax error: " + e.msg + } + } + + res := "syntax error: unexpected " + ipTokname(lookAhead) + + // To match Bison, suggest at most four expected tokens. + expected := make([]int, 0, 4) + + // Look for shiftable tokens. + base := ipPact[state] + for tok := TOKSTART; tok-1 < len(ipToknames); tok++ { + if n := base + tok; n >= 0 && n < ipLast && ipChk[ipAct[n]] == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if ipDef[state] == -2 { + i := 0 + for ipExca[i] != -1 || ipExca[i+1] != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; ipExca[i] >= 0; i += 2 { + tok := ipExca[i] + if tok < TOKSTART || ipExca[i+1] == 0 { + continue + } + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + + // If the default action is to accept or reduce, give up. + if ipExca[i+1] != 0 { + return res + } + } + + for i, tok := range expected { + if i == 0 { + res += ", expecting " + } else { + res += " or " + } + res += ipTokname(tok) + } + return res +} + +func iplex1(lex ipLexer, lval *ipSymType) (char, token int) { + token = 0 + char = lex.Lex(lval) + if char <= 0 { + token = ipTok1[0] + goto out + } + if char < len(ipTok1) { + token = ipTok1[char] + goto out + } + if char >= ipPrivate { + if char < ipPrivate+len(ipTok2) { + token = ipTok2[char-ipPrivate] + goto out + } + } + for i := 0; i < len(ipTok3); i += 2 { + token = ipTok3[i+0] + if token == char { + token = ipTok3[i+1] + goto out + } + } + +out: + if token == 0 { + token = ipTok2[1] /* unknown char */ + } + if ipDebug >= 3 { + fmt.Printf("lex %s(%d)\n", ipTokname(token), uint(char)) + } + return char, token +} + +func ipParse(iplex ipLexer) int { + return ipNewParser().Parse(iplex) +} + +func (iprcvr *ipParserImpl) Parse(iplex ipLexer) int { + var ipn int + var ipVAL ipSymType + var ipDollar []ipSymType + _ = ipDollar // silence set and not used + ipS := iprcvr.stack[:] + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + ipstate := 0 + iprcvr.char = -1 + iptoken := -1 // iprcvr.char translated into internal numbering + defer func() { + // Make sure we report no lookahead when not parsing. + ipstate = -1 + iprcvr.char = -1 + iptoken = -1 + }() + ipp := -1 + goto ipstack + +ret0: + return 0 + +ret1: + return 1 + +ipstack: + /* put a state and value onto the stack */ + if ipDebug >= 4 { + fmt.Printf("char %v in %v\n", ipTokname(iptoken), ipStatname(ipstate)) + } + + ipp++ + if ipp >= len(ipS) { + nyys := make([]ipSymType, len(ipS)*2) + copy(nyys, ipS) + ipS = nyys + } + ipS[ipp] = ipVAL + ipS[ipp].yys = ipstate + +ipnewstate: + ipn = ipPact[ipstate] + if ipn <= ipFlag { + goto ipdefault /* simple state */ + } + if iprcvr.char < 0 { + iprcvr.char, iptoken = iplex1(iplex, &iprcvr.lval) + } + ipn += iptoken + if ipn < 0 || ipn >= ipLast { + goto ipdefault + } + ipn = ipAct[ipn] + if ipChk[ipn] == iptoken { /* valid shift */ + iprcvr.char = -1 + iptoken = -1 + ipVAL = iprcvr.lval + ipstate = ipn + if Errflag > 0 { + Errflag-- + } + goto ipstack + } + +ipdefault: + /* default state action */ + ipn = ipDef[ipstate] + if ipn == -2 { + if iprcvr.char < 0 { + iprcvr.char, iptoken = iplex1(iplex, &iprcvr.lval) + } + + /* look through exception table */ + xi := 0 + for { + if ipExca[xi+0] == -1 && ipExca[xi+1] == ipstate { + break + } + xi += 2 + } + for xi += 2; ; xi += 2 { + ipn = ipExca[xi+0] + if ipn < 0 || ipn == iptoken { + break + } + } + ipn = ipExca[xi+1] + if ipn < 0 { + goto ret0 + } + } + if ipn == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + iplex.Error(ipErrorMessage(ipstate, iptoken)) + Nerrs++ + if ipDebug >= 1 { + fmt.Printf("%s", ipStatname(ipstate)) + fmt.Printf(" saw %s\n", ipTokname(iptoken)) + } + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for ipp >= 0 { + ipn = ipPact[ipS[ipp].yys] + ipErrCode + if ipn >= 0 && ipn < ipLast { + ipstate = ipAct[ipn] /* simulate a shift of "error" */ + if ipChk[ipstate] == ipErrCode { + goto ipstack + } + } + + /* the current p has no shift on "error", pop stack */ + if ipDebug >= 2 { + fmt.Printf("error recovery pops state %d\n", ipS[ipp].yys) + } + ipp-- + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if ipDebug >= 2 { + fmt.Printf("error recovery discards %s\n", ipTokname(iptoken)) + } + if iptoken == ipEofCode { + goto ret1 + } + iprcvr.char = -1 + iptoken = -1 + goto ipnewstate /* try again in the same state */ + } + } + + /* reduction by production ipn */ + if ipDebug >= 2 { + fmt.Printf("reduce %v in:\n\t%v\n", ipn, ipStatname(ipstate)) + } + + ipnt := ipn + ippt := ipp + _ = ippt // guard against "declared and not used" + + ipp -= ipR2[ipn] + // ipp is now the index of $0. Perform the default action. Iff the + // reduced production is ε, $1 is possibly out of range. + if ipp+1 >= len(ipS) { + nyys := make([]ipSymType, len(ipS)*2) + copy(nyys, ipS) + ipS = nyys + } + ipVAL = ipS[ipp+1] + + /* consult goto table to find next state */ + ipn = ipR1[ipn] + ipg := ipPgo[ipn] + ipj := ipg + ipS[ipp].yys + 1 + + if ipj >= ipLast { + ipstate = ipAct[ipg] + } else { + ipstate = ipAct[ipj] + if ipChk[ipstate] != -ipn { + ipstate = ipAct[ipg] + } + } + // dummy call; replaced with literal code + switch ipnt { + + case 1: + ipDollar = ipS[ippt-1 : ippt+1] + { + ipVAL.result = append(ipVAL.result, ipDollar[1].addrRange) + iplex.(*ipLex).output = ipVAL.result + } + case 2: + ipDollar = ipS[ippt-3 : ippt+1] + { + ipVAL.result = append(ipDollar[1].result, ipDollar[3].addrRange) + iplex.(*ipLex).output = ipVAL.result + } + case 5: + ipDollar = ipS[ippt-3 : ippt+1] + { + mask := net.CIDRMask(int(ipDollar[3].num), 32) + min := ipDollar[1].addrRange.Min.Mask(mask) + maxInt := binary.BigEndian.Uint32([]byte(min)) + + 0xffffffff - + binary.BigEndian.Uint32([]byte(mask)) + maxBytes := make([]byte, 4) + binary.BigEndian.PutUint32(maxBytes, maxInt) + maxBytes = maxBytes[len(maxBytes)-4:] + max := net.IP(maxBytes) + ipVAL.addrRange = AddressRange{ + Min: min.To4(), + Max: max.To4(), + } + } + case 6: + ipDollar = ipS[ippt-1 : ippt+1] + { + ipVAL.addrRange = ipDollar[1].addrRange + } + case 7: + ipDollar = ipS[ippt-7 : ippt+1] + { + ipVAL.addrRange = AddressRange{ + Min: net.IPv4(ipDollar[1].octRange.min, ipDollar[3].octRange.min, ipDollar[5].octRange.min, ipDollar[7].octRange.min).To4(), + Max: net.IPv4(ipDollar[1].octRange.max, ipDollar[3].octRange.max, ipDollar[5].octRange.max, ipDollar[7].octRange.max).To4(), + } + } + case 8: + ipDollar = ipS[ippt-1 : ippt+1] + { + ipVAL.octRange = octetRange{ipDollar[1].num, ipDollar[1].num} + } + case 9: + // nolint + ipDollar = ipS[ippt-1 : ippt+1] + { + ipVAL.octRange = octetRange{0, 255} + } + case 10: + ipDollar = ipS[ippt-1 : ippt+1] + { + ipVAL.octRange = ipDollar[1].octRange + } + case 11: + ipDollar = ipS[ippt-3 : ippt+1] + { + ipVAL.octRange = octetRange{ipDollar[1].num, ipDollar[3].num} + } + } + goto ipstack /* stack new state and value */ +} From 88e0f43cfd72c974e353808526260dcde8df77c6 Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 14 Apr 2022 04:38:56 +0300 Subject: [PATCH 2/5] [iprange] Fix linter. --- iprange/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iprange/README.md b/iprange/README.md index 933254a..68cc366 100644 --- a/iprange/README.md +++ b/iprange/README.md @@ -2,7 +2,7 @@ В этой задаче вам предстоит познакомиться с фаззингом, и его нативной поддержкой в go. -Нужно поправить баги в функции `ParseList`. +Нужно поправить баг в функции `ParseList`. `ParseList` принимает на вход строку с описанием рейнджей ip адрессов в одном из `n` форматов * `10.0.0.1` From a4234e2fe606825dce71496d065e4e4531182ee6 Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 14 Apr 2022 04:43:23 +0300 Subject: [PATCH 3/5] [iprange] Fix readme. --- iprange/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iprange/README.md b/iprange/README.md index 68cc366..801171b 100644 --- a/iprange/README.md +++ b/iprange/README.md @@ -9,11 +9,12 @@ * `10.0.0.0/24` * `10.0.0.*` * `10.0.0.1-10` + и возвращает список пар `(min ip, max ip)` (см. [example](./example_test.go)). Для обнаружения бага (crash функции) предлагается написать fuzz тест на функцию `ParseList`. -### Проверка решения +#### Проверка решения Во-первых должны работать имеющиеся тесты ``` From 385fc0c4a27dfaeefcfc3b8b47a70b769ba802ed Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 14 Apr 2022 04:50:22 +0300 Subject: [PATCH 4/5] [iprange] Fix readme. --- iprange/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iprange/README.md b/iprange/README.md index 801171b..b0bd00d 100644 --- a/iprange/README.md +++ b/iprange/README.md @@ -30,5 +30,5 @@ go test -v -fuzz=. ./iprange... ### Ссылки -* design draft https://go.googlesource.com/proposal/+/master/design/draft-fuzzing.md -* fuzzing tutorial в блоге go https://go.dev/doc/tutorial/fuzz +* fuzzing design draft https://go.googlesource.com/proposal/+/master/design/draft-fuzzing.md +* fuzzing tutorial https://go.dev/doc/tutorial/fuzz From 1b528a75ece344d2bcfb97c438500b4b98be6b39 Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 14 Apr 2022 04:53:49 +0300 Subject: [PATCH 5/5] [iprange] Fix readme. --- iprange/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iprange/README.md b/iprange/README.md index b0bd00d..cc536eb 100644 --- a/iprange/README.md +++ b/iprange/README.md @@ -30,5 +30,5 @@ go test -v -fuzz=. ./iprange... ### Ссылки -* fuzzing design draft https://go.googlesource.com/proposal/+/master/design/draft-fuzzing.md * fuzzing tutorial https://go.dev/doc/tutorial/fuzz +* fuzzing design draft https://go.googlesource.com/proposal/+/master/design/draft-fuzzing.md