Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

too many iterations panic #337

Open
renard opened this issue Apr 25, 2023 · 1 comment
Open

too many iterations panic #337

renard opened this issue Apr 25, 2023 · 1 comment

Comments

@renard
Copy link

renard commented Apr 25, 2023

Hello,

I am writing a A10 loadbalancer configuration parser which syntax is similar to cisco config files.

Here is a config block example:

slb virtual-server svc1.example.com 10.0.0.1
   port 80 http
      name svc1.example.com-http
      aflex HTTP_To_HTTPS
   port 443  https
      name svc1.example.com-https
      source-nat pool SNAT-10.10.0.240-254
      service-group svc1.example.com-https
      template http default-http
      template waf svc1.example.com
      template client-ssl wildcard.example.com
   port 4172  tcp
      name tcp1.example.com-tcp
      source-nat pool SNAT-10.10.0.240-254
      service-group tcp1.example.com-tcp
      template persist source-ip tpl-srcip

My go file looks like:

package main

import (
	"github.com/alecthomas/participle/v2"
	"github.com/alecthomas/participle/v2/lexer"
	"github.com/alecthomas/repr"
)

var (
	a10Lexer = lexer.MustSimple(
		[]lexer.Rule{
			{"whitespace", `\s+`, nil},
			{"eol", `[\n\r]+`, nil},
			{"Punct", `[ \t\n]`, nil},
			{"Int", `\d+`, nil},
			{"Ident", `[A-Za-z0-9._-][A-Za-z0-9._-]*`, nil},
		},
		lexer.MatchLongest(),
	)
)

// SlbVirtualServer holds virtual-server definition.
//
// virtual-server consists of a name, a bind IP address and a list of
// service definition.
//
// In A10 configuration file a virtual-server starts with:
//
//	!
//	slb virtual-server NAME IP
//	    ...
//	!
type SlbVirtualServer struct {
	Pos     lexer.Position
	Name    string                     `"slb" "virtual-server" @(Ident)`
	Ip      string                     `@(Ident)`
	Service []*SlbVirtualServerService `("port" @@)*`
}

// SlbVirtualServerService holds a virtual-server service definition.
// Each service definiton starts with:
//
//		port PORT PROTOCOL
//	        ...
//
// A service has several optional directives such as name, source-nat
// definition, service group definiton, list of templates to apply,
// etc...
type SlbVirtualServerService struct {
	// Port and Protocol appear on the same line
	Port     int    `@(Int)`
	Protocol string `@("http" | "https" | "tcp" | "udp" | "ssl-proxy")`
	// Other resources should be defined in any order.
	// However this definition generates an error:
	//
	//    panic: 10:1: too many iterations of (("name" <ident>) | ("source-nat" SlbVirtualServerServiceSourceNat) | ("service-group" <ident>) | ("template" "persist"? SlbVirtualServerServiceTemplate)* | ("aflex" <ident>)?)* (> 1000000)
	//
	Name         string                             `(  "name" @(Ident)`
	SourceNat    *SlbVirtualServerServiceSourceNat  ` | "source-nat" @@`
	ServiceGroup string                             ` | "service-group" @(Ident)`
	Templates    []*SlbVirtualServerServiceTemplate ` | ( "template" "persist"? @@ )*`
	Aflex        string                             ` | ("aflex" @(Ident))? )*`
}

// SlbVirtualServerServiceSourceNat host a service source-nat
// definiton such as:
//
//	source-nat TYPE SNAT_STRING
type SlbVirtualServerServiceSourceNat struct {
	Type string `@(Ident)`
	Name string `@(Ident)`
}

// SlbVirtualServerServiceTemplate holds a service template
// definiton such as:
//
//	      template TYPE NAME

type SlbVirtualServerServiceTemplate struct {
	Type string `@(Ident)`
	Name string `@(Ident)`
}

// newLtmVirtual parses data and creates a new SlbVirtualServer struct.
func newSlbVirtualServer(data string) (ret *SlbVirtualServer, err error) {
	ret = &SlbVirtualServer{}
	err = parseString("", data, ret)
	return
}

// parseString parses the data string into obj.
func parseString(name, data string, obj any) (err error) {
	parser := participle.MustBuild(
		obj,
		participle.Lexer(a10Lexer),
	)
	err = parser.ParseString(name, data, obj)
	return
}

func main() {
	data := `
slb virtual-server svc1.example.com 10.0.0.1
   port 80 http
      name svc1.example.com-http
      aflex HTTP_To_HTTPS
   port 443  https
      name svc1.example.com-https
      source-nat pool SNAT-10.10.0.240-254
      service-group svc1.example.com-https
      template http default-http
      template waf svc1.example.com
      template client-ssl wildcard.example.com
   port 4172  tcp
      name tcp1.example.com-tcp
      source-nat pool SNAT-10.10.0.240-254
      service-group tcp1.example.com-tcp
      template persist source-ip tpl-srcip
`
	newSlbVirtualServer, err := newSlbVirtualServer(data)
	if err != nil {
		panic(err)
	}
	repr.Println(newSlbVirtualServer)

}

However when I run it I panics:

The problem I see is that the service definition has no end delimiter. Hence I am not sure about the struct defintion:

	Port     int    `@(Int)`
	Protocol string `@("http" | "https" | "tcp" | "udp" | "ssl-proxy")`
	Name         string                             `(  "name" @(Ident)`
	SourceNat    *SlbVirtualServerServiceSourceNat  ` | "source-nat" @@`
	ServiceGroup string                             ` | "service-group" @(Ident)`
	Templates    []*SlbVirtualServerServiceTemplate ` | ( "template" "persist"? @@ )*`
	Aflex        string                             ` | ("aflex" @(Ident))? )*`

Did I missed something?

Thanks in advance.

@spatecon
Copy link
Contributor

Hi!

I can't see the panic log in your issue. Could you please provide it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants