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

Dictionary parser freeradius support #62

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func dumpAttrs(w io.Writer, c *Config, p *radius.Packet) {
case dictionary.AttributeInteger:
switch len(attr) {
case 4:
intVal := int(binary.BigEndian.Uint32(attr))
intVal := uint(binary.BigEndian.Uint32(attr))
if dictAttr != nil {
var matchedNames []string
for _, value := range dictionary.ValuesByAttribute(searchValues, dictAttr.Name) {
Expand All @@ -106,7 +106,7 @@ func dumpAttrs(w io.Writer, c *Config, p *radius.Packet) {
break
}
}
attrStr = strconv.Itoa(intVal)
attrStr = strconv.FormatUint(uint64(intVal), 10)
case 8:
attrStr = strconv.Itoa(int(binary.BigEndian.Uint64(attr)))
}
Expand Down
45 changes: 44 additions & 1 deletion dictionary/dictionary.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,37 @@ func (a *Attribute) Equals(o *Attribute) bool {
return true
}

func normalizedType(t AttributeType) AttributeType {
if t == AttributeString {
return AttributeOctets
}

return t
}

func (a *Attribute) MostlyEquals(o *Attribute) bool {
if a == o {
return true
}
if a == nil || o == nil {
return false
}

if a.Name != o.Name || !a.OID.Equals(o.OID) {
return false
}

if normalizedType(a.Type) != normalizedType(o.Type) {
return false
}

if a.FlagEncrypt != o.FlagEncrypt || a.FlagHasTag != o.FlagHasTag || a.FlagConcat != o.FlagConcat {
return false
}

return true
}

func (a *Attribute) GoString() string {
var b bytes.Buffer
b.WriteString("&dictionary.Attribute{")
Expand Down Expand Up @@ -213,7 +244,7 @@ func (a *Attribute) GoString() string {
type Value struct {
Attribute string
Name string
Number int
Number uint
}

type Vendor struct {
Expand All @@ -227,6 +258,18 @@ type Vendor struct {
Values []*Value
}

func (a *Vendor) Equals(b *Vendor) bool {
if a == b {
return true
}

if a == nil || b == nil {
return false
}

return a.Name == b.Name && a.Number == a.Number
}

func (v *Vendor) GetTypeOctets() int {
if v.TypeOctets == nil {
return 1
Expand Down
49 changes: 34 additions & 15 deletions dictionary/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ type Parser struct {

// IgnoreIdenticalAttributes specifies whether identical attributes are
// ignored, rather than a parse error being emitted.
IgnoreIdenticalAttributes bool
IgnoreIdenticalAttributes bool
IgnoreUnknownAttributeType bool
}

func (p *Parser) Parse(f File) (*Dictionary, error) {
Expand All @@ -62,6 +63,7 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File
lineNo := 1
for ; s.Scan(); lineNo++ {
line := s.Text()
line = strings.TrimLeft(line, " ")
if idx := strings.IndexByte(line, '#'); idx >= 0 {
line = line[:idx]
}
Expand All @@ -74,6 +76,12 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File
case (len(fields) == 4 || len(fields) == 5) && fields[0] == "ATTRIBUTE":
attr, err := p.parseAttribute(fields)
if err != nil {
switch err.(type) {
case *UnknownAttributeTypeError:
if p.IgnoreUnknownAttributeType {
continue
}
}
return &ParseError{
Inner: err,
File: f,
Expand All @@ -88,7 +96,7 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File
existing = AttributeByName(vendorBlock.Attributes, attr.Name)
}
if existing != nil {
if p.IgnoreIdenticalAttributes && attr.Equals(existing) {
if p.IgnoreIdenticalAttributes && attr.MostlyEquals(existing) {
break
}
return &ParseError{
Expand Down Expand Up @@ -134,18 +142,10 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File
}
}

if existing := vendorByNameOrNumber(dict.Vendors, vendor.Name, vendor.Number); existing != nil {
return &ParseError{
Inner: &DuplicateVendorError{
Vendor: vendor,
},
File: f,
Line: lineNo,
}
if existing := vendorByNameOrNumber(dict.Vendors, vendor.Name, vendor.Number); existing == nil || !vendor.Equals(existing) {
dict.Vendors = append(dict.Vendors, vendor)
}

dict.Vendors = append(dict.Vendors, vendor)

case len(fields) == 2 && fields[0] == "BEGIN-VENDOR":
// TODO: support RFC 6929 extended VSA?

Expand Down Expand Up @@ -276,6 +276,16 @@ func (p *Parser) ParseFile(filename string) (*Dictionary, error) {

func parseOID(s string) OID {
var o OID
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
num, err := strconv.ParseInt(s[2:], 16, 32)
if err != nil {
return nil
}

o = append(o, int(num))
return o
}

for i, ch := range s {
switch ch {
case '.':
Expand Down Expand Up @@ -405,6 +415,8 @@ func (p *Parser) parseAttribute(f []string) (*Attribute, error) {
Valid: true,
Bool: true,
}
case f == "virtual":
continue
default:
return nil, &UnknownAttributeFlagError{
Flag: f,
Expand All @@ -424,11 +436,18 @@ func (p *Parser) parseValue(f []string) (*Value, error) {
Name: f[2],
}

number, err := strconv.ParseInt(f[3], 10, 32)
numStr := f[3]
base := 10
if len(numStr) > 2 && (numStr[0] == '0' && (numStr[1] == 'x' || numStr[1] == 'X')) {
base = 16
numStr = numStr[2:]
}

number, err := strconv.ParseUint(numStr, base, 32)
if err != nil {
return nil, err
}
value.Number = int(number)
value.Number = uint(number)

return value, nil
}
Expand All @@ -450,7 +469,7 @@ func (p *Parser) parseVendor(f []string) (*Vendor, error) {
// "format=t,l"
// t ∈ [1, 2, 4]
// l ∈ [0, 1, 2]
if !strings.HasPrefix(f[3], "format=") || len(f[3]) != 10 || f[3][8] != ',' || (f[3][7] != '1' && f[3][7] != '2' && f[3][7] != '4') || (f[3][9] < '0' && f[3][9] > '2') {
if !strings.HasPrefix(f[3], "format=") || len(f[3]) < 10 || f[3][8] != ',' || (f[3][7] != '1' && f[3][7] != '2' && f[3][7] != '4') || (f[3][9] < '0' || f[3][9] > '2') {
return nil, &InvalidVendorFormatError{
Format: f[3],
}
Expand Down
5 changes: 5 additions & 0 deletions dictionary/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ func TestParser(t *testing.T) {
Name: "Half",
Number: 2,
},
{
Attribute: "Mode",
Name: "Quarter",
Number: 4,
},
},
}

Expand Down
4 changes: 4 additions & 0 deletions dictionary/testdata/simple.dictionary
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
ATTRIBUTE User-Name 1 string
ATTRIBUTE User-Password 2 octets encrypt=1

#Skip comment
#Skip comment with spaces in front

ATTRIBUTE Mode 127 integer
VALUE Mode Full 1
VALUE Mode Half 2
VALUE Mode Quarter 0x4

ATTRIBUTE ARAP-Challenge-Response 84 octets[8]
2 changes: 1 addition & 1 deletion dictionarygen/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ func (g *Generator) genAttributeInteger(w io.Writer, attr *dictionary.Attribute,
p(w, `const (`)
for _, value := range values {
valueIdent := identifier(value.Name)
p(w, ` `, ident, `_Value_`, valueIdent, ` `, ident, ` = `, strconv.Itoa(value.Number))
p(w, ` `, ident, `_Value_`, valueIdent, ` `, ident, ` = `, strconv.FormatUint(uint64(value.Number), 10))
}
p(w, `)`)
}
Expand Down
2 changes: 1 addition & 1 deletion dictionarygen/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (g *Generator) Generate(dict *dictionary.Dictionary) ([]byte, error) {
for _, value := range exAttr.Values {
attrIdent := identifier(value.Attribute)
valueIdent := identifier(value.Name)
p(&w, ` `, attrIdent, `_Value_`, valueIdent, ` `, attrIdent, ` = `, strconv.Itoa(value.Number))
p(&w, ` `, attrIdent, `_Value_`, valueIdent, ` `, attrIdent, ` = `, strconv.FormatUint(uint64(value.Number), 10))
}
p(&w, `)`)
}
Expand Down