/
nmap_match_parser.go
132 lines (113 loc) · 2.67 KB
/
nmap_match_parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package fp
import (
"bufio"
"bytes"
"github.com/pkg/errors"
)
type DataBlock struct {
Name string
Content []byte
Option []byte
}
type Rule struct {
Type string
ServiceName string
DataBlocks map[byte]*DataBlock
CpeBlocks []*DataBlock
}
const (
delimiter = ' '
)
const (
statInit = 0
statStart = 1
statInData = 2
statStartData = 3
statSetDataDelimiter = 4
statSetDataBlockOptions = 5
)
func parseMatchRule(line []byte) (*Rule, error) {
scanner := bufio.NewScanner(bytes.NewBuffer(line))
scanner.Split(bufio.ScanBytes)
var (
dataBlockNameBuffer []byte
ch byte
state int
dataDelimiter byte
currentDataBlock = &DataBlock{}
cpeBlocks []*DataBlock
)
rule := &Rule{
DataBlocks: map[byte]*DataBlock{},
}
for scanner.Scan() {
ch = scanner.Bytes()[0]
switch state {
case statSetDataBlockOptions:
if delimiter == ch {
state = statStartData
continue
}
currentDataBlock.Option = append(currentDataBlock.Option, ch)
continue
case statInData:
if ch == dataDelimiter {
state = statSetDataBlockOptions
continue
}
currentDataBlock.Content = append(currentDataBlock.Content, ch)
continue
case statSetDataDelimiter:
dataDelimiter = ch
state = statInData
continue
case statStartData:
// p/v/i/h/o/d/cpe
if bytes.Contains([]byte{'p', 'v', 'i', 'h', 'o', 'd', 'm'}, []byte{ch}) && len(dataBlockNameBuffer) <= 0 {
state = statSetDataDelimiter
currentDataBlock = &DataBlock{Name: string(ch)}
rule.DataBlocks[ch] = currentDataBlock
continue
}
if len(dataBlockNameBuffer) > 4 {
return nil, errors.Errorf("parse data block failed, buffer failed: %s", string(append(dataBlockNameBuffer, ch)))
}
dataBlockNameBuffer = append(dataBlockNameBuffer, ch)
if len(dataBlockNameBuffer) >= 4 {
if len(dataBlockNameBuffer) == 4 {
if string(dataBlockNameBuffer) == "cpe:" {
state = statSetDataDelimiter
currentDataBlock = &DataBlock{Name: "cpe"}
dataBlockNameBuffer = []byte{}
cpeBlocks = append(cpeBlocks, currentDataBlock)
continue
}
} else {
dataBlockNameBuffer = []byte{}
}
} else {
continue
}
case statInit:
if delimiter == ch {
if rule.Type == "match" || rule.Type == "softmatch" {
state = statStart
continue
}
return nil, errors.New("first pattern failed")
}
rule.Type += string(ch)
continue
case statStart:
if delimiter == ch {
state = statStartData
continue
}
rule.ServiceName += string(ch)
continue
default:
}
}
rule.CpeBlocks = cpeBlocks
return rule, nil
}