mirror of
				https://github.com/fluffle/goirc
				synced 2025-11-03 19:48:04 +00:00 
			
		
		
		
	Ragel based config file parser, ftw.
This commit is contained in:
		
							parent
							
								
									377fbcd3af
								
							
						
					
					
						commit
						22ccce6bb2
					
				
					 9 changed files with 250 additions and 366 deletions
				
			
		| 
						 | 
				
			
			@ -7,11 +7,10 @@ include $(GOROOT)/src/Make.inc
 | 
			
		|||
TARG=irc/server/config
 | 
			
		||||
GOFILES=\
 | 
			
		||||
	config.go\
 | 
			
		||||
	port.go\
 | 
			
		||||
	oper.go\
 | 
			
		||||
	link.go\
 | 
			
		||||
	ban.go\
 | 
			
		||||
	info.go\
 | 
			
		||||
    settings.go\
 | 
			
		||||
    parser.go\
 | 
			
		||||
 | 
			
		||||
include $(GOROOT)/src/Make.pkg
 | 
			
		||||
 | 
			
		||||
parser.go: parser.rl
 | 
			
		||||
	ragel -Z -G2 -o parser.go parser.rl
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,33 +0,0 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
type cBan interface {
 | 
			
		||||
	Match(string) bool
 | 
			
		||||
	Reason() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// G-Line etc; 
 | 
			
		||||
type cBanNick struct {
 | 
			
		||||
	NickMask string // nick!ident@host
 | 
			
		||||
	Reason   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Z-Line
 | 
			
		||||
type cBanIP struct {
 | 
			
		||||
	Address string // ip (or hostname), plus optional CIDR netmask
 | 
			
		||||
	Reason  string
 | 
			
		||||
	ip		string // parsed into these
 | 
			
		||||
	cidr	int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CTCP version ban
 | 
			
		||||
type cBanVersion struct {
 | 
			
		||||
	VersionRegex string // regex to match against version reply
 | 
			
		||||
	Reason       string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ban server from linking to network
 | 
			
		||||
type cBanServer struct {
 | 
			
		||||
	ServerMask string // matched against name of linked server
 | 
			
		||||
	Reason     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,14 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"scanner"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	fn string
 | 
			
		||||
	scan *scanner.Scanner
 | 
			
		||||
 | 
			
		||||
	// Ports we listen on.
 | 
			
		||||
	Ports map[int]*cPort
 | 
			
		||||
| 
						 | 
				
			
			@ -31,17 +29,6 @@ type Config struct {
 | 
			
		|||
	Errors []os.Error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type configMap  map[string]func(*Config)
 | 
			
		||||
type keywordMap map[string]func(*Config, interface{})
 | 
			
		||||
 | 
			
		||||
var configKeywords = configMap{
 | 
			
		||||
	"port": (*Config).parsePort,
 | 
			
		||||
	"oper": (*Config).parseOper,
 | 
			
		||||
//	"link": (*Config).parseLink,
 | 
			
		||||
//	"ban":  (*Config).parseBan,
 | 
			
		||||
//	"info": (*Config).parseInfo,
 | 
			
		||||
//	"set":  (*Config).parseSettings,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func LoadConfig(filename string) *Config {
 | 
			
		||||
	conf := &Config{fn: filename}
 | 
			
		||||
| 
						 | 
				
			
			@ -74,103 +61,129 @@ func (conf *Config) Rehash() {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) Parse(io io.Reader) {
 | 
			
		||||
	s := &scanner.Scanner{}
 | 
			
		||||
	s.Init(io)
 | 
			
		||||
	s.Filename = conf.fn
 | 
			
		||||
	conf.scan = s
 | 
			
		||||
	tok, text := conf.next()
 | 
			
		||||
	for tok != scanner.EOF {
 | 
			
		||||
		// This external loop should only parse Config things
 | 
			
		||||
		if f, ok := configKeywords[text]; ok {
 | 
			
		||||
			f(conf)
 | 
			
		||||
		} else {
 | 
			
		||||
			conf.parseError("Invalid top-level keyword '%s'", text)
 | 
			
		||||
/* Port configuration */
 | 
			
		||||
type cPort struct {
 | 
			
		||||
	Port     int
 | 
			
		||||
	BindIP   net.IP // bind to a specific IP for listen port
 | 
			
		||||
	Class    string // "server" or "client"
 | 
			
		||||
 | 
			
		||||
	// Is port a tls.Listener? Does it support compression (no)?
 | 
			
		||||
	SSL, Zip bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultPort() *cPort {
 | 
			
		||||
	return &cPort{
 | 
			
		||||
		BindIP: nil, Class: "client",
 | 
			
		||||
		SSL: false, Zip: false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *cPort) String() string {
 | 
			
		||||
    str := []string{fmt.Sprintf("port %d {", p.Port)}
 | 
			
		||||
	if p.BindIP != nil {
 | 
			
		||||
		str = append(str,
 | 
			
		||||
			fmt.Sprintf("\tbind_ip = %s", p.BindIP.String()))
 | 
			
		||||
	}
 | 
			
		||||
	str = append(str,
 | 
			
		||||
		fmt.Sprintf("\tclass  = %s", p.Class),
 | 
			
		||||
		fmt.Sprintf("\tssl    = %t", p.SSL),
 | 
			
		||||
		fmt.Sprintf("\tzip    = %t", p.Zip),
 | 
			
		||||
		"}",
 | 
			
		||||
	)
 | 
			
		||||
	return strings.Join(str, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Oper configuration */
 | 
			
		||||
type cOper struct {
 | 
			
		||||
	Username, Password string
 | 
			
		||||
	HostMask []string
 | 
			
		||||
 | 
			
		||||
	// Permissions for oper
 | 
			
		||||
	CanKill, CanBan, CanRenick, CanLink  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultOper() *cOper {
 | 
			
		||||
	return &cOper{
 | 
			
		||||
		HostMask: []string{},
 | 
			
		||||
		CanKill: true, CanBan: true,
 | 
			
		||||
		CanRenick: false, CanLink: false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *cOper) String() string {
 | 
			
		||||
    str := []string{fmt.Sprintf("oper %s {", o.Username)}
 | 
			
		||||
	str = append(str, fmt.Sprintf("\tpassword = %s", o.Password))
 | 
			
		||||
	if len(o.HostMask) == 0 {
 | 
			
		||||
		str = append(str, fmt.Sprintf("\thostmask = *@*"))
 | 
			
		||||
	} else {
 | 
			
		||||
		for _, h := range o.HostMask {
 | 
			
		||||
			str = append(str, fmt.Sprintf("\thostmask = %s", h))
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("Token: '%s', type %s\n", s.TokenText(), scanner.TokenString(tok))
 | 
			
		||||
		tok, text = conf.next()
 | 
			
		||||
	}
 | 
			
		||||
	str = append(str,
 | 
			
		||||
		fmt.Sprintf("\tkill    = %t", o.CanKill),
 | 
			
		||||
		fmt.Sprintf("\tban     = %t", o.CanBan),
 | 
			
		||||
		fmt.Sprintf("\trenick  = %t", o.CanRenick),
 | 
			
		||||
		fmt.Sprintf("\tlink    = %t", o.CanLink),
 | 
			
		||||
		"}",
 | 
			
		||||
	)
 | 
			
		||||
	return strings.Join(str, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseKwBlock(dst interface{}, bt string, kw keywordMap) {
 | 
			
		||||
	if ok := conf.expect("{"); !ok {
 | 
			
		||||
		conf.parseError("Expected %s configuration block.", bt)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	tok, text := conf.next()
 | 
			
		||||
	for tok != scanner.EOF {
 | 
			
		||||
		if f, ok := kw[text]; ok {
 | 
			
		||||
			if ok = conf.expect("="); ok {
 | 
			
		||||
				f(conf, dst)
 | 
			
		||||
			}
 | 
			
		||||
		} else if text == "}" {
 | 
			
		||||
			break
 | 
			
		||||
		} else {
 | 
			
		||||
			conf.parseError("Invalid %s keyword '%s'", bt, text)
 | 
			
		||||
		}
 | 
			
		||||
		tok, text = conf.next()
 | 
			
		||||
	}
 | 
			
		||||
/* Link configuration */
 | 
			
		||||
type cLink struct {
 | 
			
		||||
	Server      string // Server name for link
 | 
			
		||||
	Address     string // {ip,ip6,host}:port
 | 
			
		||||
	ReceivePass string // Password when server connects to us 
 | 
			
		||||
	ConnectPass string // Password when we connect to server
 | 
			
		||||
 | 
			
		||||
	// Do we use tls.Dial? or compression (no)? Do we auto-connect on start?
 | 
			
		||||
	SSL, Zip, Auto bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var booleans = map[string]bool {
 | 
			
		||||
	"true": true,
 | 
			
		||||
	"yes": true,
 | 
			
		||||
	"on": true,
 | 
			
		||||
	"1": true,
 | 
			
		||||
	"false": false,
 | 
			
		||||
	"no": false,
 | 
			
		||||
	"off": false,
 | 
			
		||||
	"0": false,
 | 
			
		||||
/* Static ban configuration */
 | 
			
		||||
type cBan interface {
 | 
			
		||||
	Match(string) bool
 | 
			
		||||
	Reason() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) expectBool() (bool, bool) {
 | 
			
		||||
	tok, text := conf.next()
 | 
			
		||||
	if val, ok := booleans[text]; tok == scanner.Ident && ok {
 | 
			
		||||
		return val, ok
 | 
			
		||||
	}
 | 
			
		||||
	conf.parseError("Expected boolean, got '%s'", text)
 | 
			
		||||
	return false, false
 | 
			
		||||
// G-Line etc; 
 | 
			
		||||
type cBanNick struct {
 | 
			
		||||
	NickMask string // nick!ident@host
 | 
			
		||||
	Reason   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) expectInt() (int, bool) {
 | 
			
		||||
	tok, text := conf.next()
 | 
			
		||||
	num, err := strconv.Atoi(text)
 | 
			
		||||
	if tok != scanner.Int || err != nil {
 | 
			
		||||
		conf.parseError("Expected integer, got '%s'", text)
 | 
			
		||||
		return 0, false
 | 
			
		||||
	}
 | 
			
		||||
	return num, true
 | 
			
		||||
// Z-Line
 | 
			
		||||
type cBanIP struct {
 | 
			
		||||
	Address string // ip (or hostname), plus optional CIDR netmask
 | 
			
		||||
	Reason  string
 | 
			
		||||
	ip		string // parsed into these
 | 
			
		||||
	cidr	int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) expectString() (string, bool) {
 | 
			
		||||
	tok, text := conf.next()
 | 
			
		||||
	if tok != scanner.String && tok != scanner.Ident {
 | 
			
		||||
		conf.parseError("Expected string, got '%s'", text)
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
	return text, true
 | 
			
		||||
// CTCP version ban
 | 
			
		||||
type cBanVersion struct {
 | 
			
		||||
	VersionRegex string // regex to match against version reply
 | 
			
		||||
	Reason       string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) expect(str string) bool {
 | 
			
		||||
	_, text := conf.next()
 | 
			
		||||
	if text != str {
 | 
			
		||||
		conf.parseError("Expected '%s', got '%s'", str, text)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
// Ban server from linking to network
 | 
			
		||||
type cBanServer struct {
 | 
			
		||||
	ServerMask string // matched against name of linked server
 | 
			
		||||
	Reason     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) next() (int, string) {
 | 
			
		||||
	tok := conf.scan.Scan()
 | 
			
		||||
	text := conf.scan.TokenText()
 | 
			
		||||
	if tok == scanner.String {
 | 
			
		||||
		// drop "quotes" -> quotes
 | 
			
		||||
		text = text[1:len(text)-1]
 | 
			
		||||
	}
 | 
			
		||||
	return tok, text
 | 
			
		||||
/* IRCd settings */
 | 
			
		||||
type cSettings struct {
 | 
			
		||||
	SSLKey, SSLCert, SSLCACert string
 | 
			
		||||
	MaxChans, MaxConnsPerIP int
 | 
			
		||||
	LogFile string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseError(err string, args ...interface{}) {
 | 
			
		||||
	err = conf.scan.Pos().String() + ": " + err
 | 
			
		||||
	conf.Errors = append(conf.Errors, os.NewError(fmt.Sprintf(err, args...)))
 | 
			
		||||
/* IRCd information */
 | 
			
		||||
type cInfo struct {
 | 
			
		||||
	Name, Network, Info, MOTDFile string
 | 
			
		||||
	Admins []string
 | 
			
		||||
	Numeric int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +0,0 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
type cInfo struct {
 | 
			
		||||
	Name, Network, Info, MOTDFile string
 | 
			
		||||
	Admins []string
 | 
			
		||||
	Numeric int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +0,0 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
type cLink struct {
 | 
			
		||||
	Server      string // Server name for link
 | 
			
		||||
	Address     string // {ip,ip6,host}:port
 | 
			
		||||
	ReceivePass string // Password when server connects to us 
 | 
			
		||||
	ConnectPass string // Password when we connect to server
 | 
			
		||||
 | 
			
		||||
	// Do we use tls.Dial? or compression (no)? Do we auto-connect on start?
 | 
			
		||||
	SSL, Zip, Auto bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,106 +0,0 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"scanner"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type cOper struct {
 | 
			
		||||
	Username, Password string
 | 
			
		||||
	HostMask []string
 | 
			
		||||
 | 
			
		||||
	// Permissions for oper
 | 
			
		||||
	CanKill, CanBan, CanRenick, CanLink  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var operKeywords = keywordMap{
 | 
			
		||||
	"password": (*Config).parseOperPassword,
 | 
			
		||||
	"hostmask": (*Config).parseOperHostMask,
 | 
			
		||||
	"kill": (*Config).parseOperKill,
 | 
			
		||||
	"ban": (*Config).parseOperBan,
 | 
			
		||||
	"renick": (*Config).parseOperRenick,
 | 
			
		||||
	"link": (*Config).parseOperLink,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultOper() *cOper {
 | 
			
		||||
	return &cOper{
 | 
			
		||||
		HostMask: []string{},
 | 
			
		||||
		CanKill: true, CanBan: true,
 | 
			
		||||
		CanRenick: false, CanLink: false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *cOper) String() string {
 | 
			
		||||
    str := []string{fmt.Sprintf("oper \"%s\" {", o.Username)}
 | 
			
		||||
	str = append(str, fmt.Sprintf("\tpassword = \"%s\"", o.Password))
 | 
			
		||||
	if len(o.HostMask) == 0 {
 | 
			
		||||
		str = append(str, fmt.Sprintf("\thostmask = \"*@*\""))
 | 
			
		||||
	} else {
 | 
			
		||||
		for _, h := range o.HostMask {
 | 
			
		||||
			str = append(str, fmt.Sprintf("\thostmask = \"%s\"", h))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	str = append(str,
 | 
			
		||||
		fmt.Sprintf("\tkill    = %t", o.CanKill),
 | 
			
		||||
		fmt.Sprintf("\tban     = %t", o.CanBan),
 | 
			
		||||
		fmt.Sprintf("\trenick  = %t", o.CanRenick),
 | 
			
		||||
		fmt.Sprintf("\tlink    = %t", o.CanLink),
 | 
			
		||||
		"}",
 | 
			
		||||
	)
 | 
			
		||||
	return strings.Join(str, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseOper() {
 | 
			
		||||
	oper := defaultOper()
 | 
			
		||||
	tok, text := conf.next()
 | 
			
		||||
	if tok != scanner.String && tok != scanner.Ident {
 | 
			
		||||
		conf.parseError("Invalid username '%s'", text)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	oper.Username = text
 | 
			
		||||
	conf.parseKwBlock(oper, "oper", operKeywords)
 | 
			
		||||
	fmt.Println(oper.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseOperPassword(oi interface{}) {
 | 
			
		||||
	oper := oi.(*cOper)
 | 
			
		||||
	if pass, ok := conf.expectString(); ok {
 | 
			
		||||
		oper.Password = pass
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseOperHostMask(oi interface{}) {
 | 
			
		||||
	oper := oi.(*cOper)
 | 
			
		||||
	if mask, ok := conf.expectString(); ok {
 | 
			
		||||
		oper.HostMask = append(oper.HostMask, mask)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseOperKill(oi interface{}) {
 | 
			
		||||
	oper := oi.(*cOper)
 | 
			
		||||
	if kill, ok := conf.expectBool(); ok {
 | 
			
		||||
		oper.CanKill = kill
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseOperBan(oi interface{}) {
 | 
			
		||||
	oper := oi.(*cOper)
 | 
			
		||||
	if ban, ok := conf.expectBool(); ok {
 | 
			
		||||
		oper.CanBan = ban
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseOperRenick(oi interface{}) {
 | 
			
		||||
	oper := oi.(*cOper)
 | 
			
		||||
	if renick, ok := conf.expectBool(); ok {
 | 
			
		||||
		oper.CanRenick = renick
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parseOperLink(oi interface{}) {
 | 
			
		||||
	oper := oi.(*cOper)
 | 
			
		||||
	if link, ok := conf.expectBool(); ok {
 | 
			
		||||
		oper.CanLink = link
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										136
									
								
								server/config/parser.rl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								server/config/parser.rl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,136 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
%% machine config;
 | 
			
		||||
%% write data;
 | 
			
		||||
 | 
			
		||||
var booleans = map[string]bool {
 | 
			
		||||
	"true": true,
 | 
			
		||||
	"yes": true,
 | 
			
		||||
	"on": true,
 | 
			
		||||
	"1": true,
 | 
			
		||||
	"false": false,
 | 
			
		||||
	"no": false,
 | 
			
		||||
	"off": false,
 | 
			
		||||
	"0": false,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
%%{
 | 
			
		||||
	# mark the current position for acquiring data
 | 
			
		||||
	action mark { mark = p }
 | 
			
		||||
 | 
			
		||||
	# acceptable boolean values
 | 
			
		||||
	true = "yes" | "on" | "1" | "true" ;
 | 
			
		||||
	false = "no" | "off" | "0" | "false" ;
 | 
			
		||||
	boolean = true | false ;
 | 
			
		||||
 | 
			
		||||
	# IP addresses, v4 and v6
 | 
			
		||||
	octet = digit         # 0-9
 | 
			
		||||
		| digit digit       # 00-99
 | 
			
		||||
		| [01] digit digit  # 000-199
 | 
			
		||||
		| "2" [0-4] digit   # 200-249
 | 
			
		||||
		| "2" "5" [0-5] ;   # 250-255
 | 
			
		||||
	ipv4addr = octet "." octet "." octet "." octet ;
 | 
			
		||||
	ipv6addr = ( xdigit{1,4} ":" ){7} xdigit{1,4}      # all 8 blocks
 | 
			
		||||
		| xdigit{0,4} "::"                               # special case
 | 
			
		||||
		| ( xdigit{1,4} ":" ){1,6} ":"                   # first 1-6, ::
 | 
			
		||||
		| ":" ( ":" xdigit{1,4} ){1,6}                   # ::, final 1-6
 | 
			
		||||
		| xdigit{1,4} ":" ( ":" xdigit{1,4} ){1,6}       # 1::1-6
 | 
			
		||||
		| ( xdigit{1,4} ":" ){2}( ":" xdigit{1,4} ){1,5} # 2::1-5
 | 
			
		||||
		| ( xdigit{1,4} ":" ){3}( ":" xdigit{1,4} ){1,4} # 3::1-4
 | 
			
		||||
		| ( xdigit{1,4} ":" ){4}( ":" xdigit{1,4} ){1,5} # 4::1-3
 | 
			
		||||
		| ( xdigit{1,4} ":" ){5}( ":" xdigit{1,4} ){1,2} # 5::1-2
 | 
			
		||||
		| ( xdigit{1,4} ":" ){6}( ":" xdigit{1,4} ) ;    # 6::1
 | 
			
		||||
 | 
			
		||||
	# Actions to create a cPort and save it into the Config struct
 | 
			
		||||
	action new_port { cur = defaultPort() }
 | 
			
		||||
	action save_port {
 | 
			
		||||
		port := cur.(*cPort)
 | 
			
		||||
		conf.Ports[port.Port] = port
 | 
			
		||||
		cur = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# parse and save the port number
 | 
			
		||||
	action set_portnum {
 | 
			
		||||
		cur.(*cPort).Port, _ = strconv.Atoi(string(data[mark:p]))
 | 
			
		||||
	}
 | 
			
		||||
	portnum = digit+ >mark %set_portnum ;
 | 
			
		||||
 | 
			
		||||
	# parse a bind_ip statement and save the IP
 | 
			
		||||
	action set_bindip {
 | 
			
		||||
		cur.(*cPort).BindIP = net.ParseIP(string(data[mark:p]))
 | 
			
		||||
	}
 | 
			
		||||
	bindip = (ipv4addr | ipv6addr) >mark %set_bindip ;
 | 
			
		||||
	portbindip = "bind_ip" " "+ "=" " "+ bindip ;
 | 
			
		||||
 | 
			
		||||
	# parse a class statement and save it
 | 
			
		||||
	action set_class {
 | 
			
		||||
		cur.(*cPort).Class = string(data[mark:p])
 | 
			
		||||
	}
 | 
			
		||||
	portclass = "class" " "+ "=" " "+ ("server" | "client" >mark %set_class) ;
 | 
			
		||||
 | 
			
		||||
	# parse SSL and Zip booleans
 | 
			
		||||
	action set_ssl {
 | 
			
		||||
		cur.(*cPort).SSL = booleans[string(data[mark:p])]
 | 
			
		||||
	}
 | 
			
		||||
	portssl = "ssl" " "+ "=" " "+ (boolean >mark %set_ssl) ;
 | 
			
		||||
	action set_zip {
 | 
			
		||||
		cur.(*cPort).Zip = booleans[string(data[mark:p])]
 | 
			
		||||
	}
 | 
			
		||||
	portzip = "zip" " "+ "=" " "+ (boolean >mark %set_zip) ;
 | 
			
		||||
 | 
			
		||||
	portstmt = ( portbindip | portclass | portssl | portzip ) ;
 | 
			
		||||
	portblock = "{" space* ( portstmt | (portstmt " "* "\n" space* )+ ) space* "}" ;
 | 
			
		||||
 | 
			
		||||
	# a port configuration can either be:
 | 
			
		||||
	# port <portnum>\n
 | 
			
		||||
	# port <portnum> { portblock }
 | 
			
		||||
	basicport = "port" >new_port " "+ portnum " "* "\n" %save_port;
 | 
			
		||||
	portdefn = "port" >new_port " "+ portnum " "+ portblock %save_port ;
 | 
			
		||||
	portconfig = space* ( basicport | portdefn ) space*;
 | 
			
		||||
 | 
			
		||||
#	config = portconfig+
 | 
			
		||||
#	  | operconfig+
 | 
			
		||||
#	  | linkconfig*
 | 
			
		||||
#	  | infoconfig
 | 
			
		||||
#	  | settings ;
 | 
			
		||||
 | 
			
		||||
	main := portconfig+;
 | 
			
		||||
}%%
 | 
			
		||||
 | 
			
		||||
func (conf *Config) Parse(r io.Reader) {
 | 
			
		||||
	cs, p, mark, pe, eof, buflen := 0, 0, 0, 0, 0, 16384
 | 
			
		||||
	done := false
 | 
			
		||||
	var cur interface{}
 | 
			
		||||
	data := make([]byte, buflen)
 | 
			
		||||
 | 
			
		||||
	%% write init;
 | 
			
		||||
 | 
			
		||||
	for !done {
 | 
			
		||||
		n, err := r.Read(data)
 | 
			
		||||
		pe = p + n
 | 
			
		||||
		if err == os.EOF {
 | 
			
		||||
			fmt.Println("yeahhhhh.")
 | 
			
		||||
			done = true
 | 
			
		||||
			eof = pe
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		%% write exec;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cs < config_first_final {
 | 
			
		||||
		fmt.Printf("Parse error at %d near '%s'\n", p, data[p:p+10])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, port := range conf.Ports {
 | 
			
		||||
		fmt.Println(port.String())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,97 +0,0 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"scanner"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type cPort struct {
 | 
			
		||||
	Port     int
 | 
			
		||||
	BindIP   net.IP // bind to a specific IP for listen port
 | 
			
		||||
	Class    string // "server" or "client"
 | 
			
		||||
 | 
			
		||||
	// Is port a tls.Listener? Does it support compression (no)?
 | 
			
		||||
	SSL, Zip bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var portKeywords = keywordMap{
 | 
			
		||||
	"bind_ip": (*Config).parsePortBindIP,
 | 
			
		||||
	"class": (*Config).parsePortClass,
 | 
			
		||||
	"ssl": (*Config).parsePortSSL,
 | 
			
		||||
	"zip": (*Config).parsePortZip,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultPort() *cPort {
 | 
			
		||||
	return &cPort{
 | 
			
		||||
		BindIP: nil, Class: "client",
 | 
			
		||||
		SSL: false, Zip: false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *cPort) String() string {
 | 
			
		||||
    str := []string{fmt.Sprintf("port %d {", p.Port)}
 | 
			
		||||
	if p.BindIP != nil {
 | 
			
		||||
		str = append(str,
 | 
			
		||||
			fmt.Sprintf("\tbind_ip = \"%s\"", p.BindIP.String()))
 | 
			
		||||
	}
 | 
			
		||||
	str = append(str,
 | 
			
		||||
		fmt.Sprintf("\tclass  = \"%s\"", p.Class),
 | 
			
		||||
		fmt.Sprintf("\tssl    = %t", p.SSL),
 | 
			
		||||
		fmt.Sprintf("\tzip    = %t", p.Zip),
 | 
			
		||||
		"}",
 | 
			
		||||
	)
 | 
			
		||||
	return strings.Join(str, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parsePort() {
 | 
			
		||||
	port := defaultPort()
 | 
			
		||||
	portnum, ok := conf.expectInt()
 | 
			
		||||
	if !ok || portnum > 65535 || portnum < 1024 {
 | 
			
		||||
		conf.parseError("Invalid port '%s'", portnum)
 | 
			
		||||
		port = nil
 | 
			
		||||
	} else {
 | 
			
		||||
		port.Port = portnum
 | 
			
		||||
		conf.Ports[portnum] = port
 | 
			
		||||
	}
 | 
			
		||||
	if conf.scan.Peek() != '\n' {
 | 
			
		||||
		conf.parseKwBlock(port, "port", portKeywords)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println(port.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parsePortBindIP(pi interface{}) {
 | 
			
		||||
	port := pi.(*cPort)
 | 
			
		||||
	_, text := conf.next()
 | 
			
		||||
	if ip := net.ParseIP(text); ip != nil {
 | 
			
		||||
		port.BindIP = ip
 | 
			
		||||
	} else {
 | 
			
		||||
		conf.parseError("'%s' is not a valid IP address", text)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parsePortClass(pi interface{}) {
 | 
			
		||||
	port := pi.(*cPort)
 | 
			
		||||
	tok, text := conf.next()
 | 
			
		||||
	if tok == scanner.String && (text == "server" || text == "client") {
 | 
			
		||||
		port.Class = text
 | 
			
		||||
	} else {
 | 
			
		||||
		conf.parseError(
 | 
			
		||||
			"Port class must be \"server\" or \"client\", got '%s'", text)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parsePortSSL(pi interface{}) {
 | 
			
		||||
	port := pi.(*cPort)
 | 
			
		||||
	if ssl, ok := conf.expectBool(); ok {
 | 
			
		||||
		port.SSL = ssl
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (conf *Config) parsePortZip(pi interface{}) {
 | 
			
		||||
	port := pi.(*cPort)
 | 
			
		||||
	if zip, ok := conf.expectBool(); ok {
 | 
			
		||||
		port.Zip = zip
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +0,0 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
type cSettings struct {
 | 
			
		||||
	SSLKey, SSLCert, SSLCACert string
 | 
			
		||||
	MaxChans, MaxConnsPerIP int
 | 
			
		||||
	LogFile string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue