Skip to content

Commit 0ba2de9

Browse files
added ParseNextLayer function
1 parent 8c00df1 commit 0ba2de9

File tree

3 files changed

+148
-17
lines changed

3 files changed

+148
-17
lines changed

layers/dns.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"strings"
99
)
1010

11+
// TODO (shadowy-pycoder): add MarshalJSON
12+
1113
const headerSizeDNS = 12
1214

1315
type DNSFlags struct {
@@ -835,6 +837,9 @@ func parseRData(payload, tail []byte, typ uint16, rdl int) (fmt.Stringer, []byte
835837
DataLen: uint16(rdl),
836838
}
837839
case 65:
840+
if len(tail) < 3 {
841+
return nil, nil, ErrSliceBounds
842+
}
838843
priority := binary.BigEndian.Uint16(tail[0:2])
839844
nameLength := tail[2]
840845
var target string

layers/layers.go

Lines changed: 141 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,30 @@
22
package layers
33

44
import (
5+
"bytes"
6+
"encoding/binary"
57
"fmt"
8+
"net/netip"
69
"unsafe"
710
)
811

912
const maxLenSummary = 110
1013

11-
var LayerMap = map[string]Layer{
12-
"ETH": &EthernetFrame{},
13-
"IPv4": &IPv4Packet{},
14-
"IPv6": &IPv6Packet{},
15-
"ARP": &ARPPacket{},
16-
"TCP": &TCPSegment{},
17-
"UDP": &UDPSegment{},
18-
"ICMP": &ICMPSegment{},
19-
"ICMPv6": &ICMPv6Segment{},
20-
"DNS": &DNSMessage{},
21-
"FTP": &FTPMessage{},
22-
"HTTP": &HTTPMessage{},
23-
"SNMP": &SNMPMessage{},
24-
"SSH": &SSHMessage{},
25-
"TLS": &TLSMessage{},
14+
var Layers = []string{
15+
"ETH",
16+
"IPv4",
17+
"IPv6",
18+
"ARP",
19+
"TCP",
20+
"UDP",
21+
"ICMP",
22+
"ICMPv6",
23+
"DNS",
24+
"FTP",
25+
"HTTP",
26+
"SNMP",
27+
"SSH",
28+
"TLS",
2629
}
2730

2831
var (
@@ -46,6 +49,129 @@ type Layer interface {
4649
Summary() string
4750
}
4851

52+
func parseNextLayerFallback(data []byte) Layer {
53+
for _, layer := range Layers {
54+
next := GetNextLayer(layer)
55+
if err := next.Parse(data); err == nil {
56+
return next
57+
}
58+
}
59+
return nil
60+
}
61+
62+
func parseNextLayerFromBytes(data []byte) Layer {
63+
buf := make([]byte, 0, len(data))
64+
buf = append(buf, data...)
65+
var next Layer
66+
firstByte := buf[0]
67+
if firstByte >= 0x45 && firstByte <= 0x4F {
68+
next = GetNextLayer("IPv4")
69+
if err := next.Parse(buf); err == nil {
70+
return next
71+
}
72+
}
73+
if firstByte>>4 == 6 {
74+
next = GetNextLayer("IPv6")
75+
if err := next.Parse(buf); err == nil {
76+
return next
77+
}
78+
}
79+
if firstByte == HandshakeTLSVal {
80+
next = GetNextLayer("TLS")
81+
if err := next.Parse(buf); err == nil {
82+
return next
83+
}
84+
}
85+
if len(buf) > 3 {
86+
b1 := binary.BigEndian.Uint16(buf[0:2])
87+
b2 := binary.BigEndian.Uint16(buf[2:4])
88+
if b1 == 1 && (b2 == 0x0800 || b2 == 0x86dd) {
89+
next = GetNextLayer("ARP")
90+
if err := next.Parse(buf); err == nil {
91+
return next
92+
}
93+
}
94+
}
95+
if len(buf) > 15 {
96+
b1 := binary.BigEndian.Uint16(buf[12:14])
97+
if b1 == 0x0806 || b1 == 0x0800 || b1 == 0x86dd {
98+
next = GetNextLayer("ETH")
99+
if err := next.Parse(buf); err == nil {
100+
return next
101+
}
102+
}
103+
}
104+
if bytes.Contains(buf, protohttp10) || bytes.Contains(buf, protohttp11) {
105+
next = GetNextLayer("HTTP")
106+
if err := next.Parse(buf); err == nil {
107+
return next
108+
}
109+
}
110+
if bytes.Contains(buf, protoSSH) {
111+
next = GetNextLayer("SSH")
112+
if err := next.Parse(buf); err == nil {
113+
return next
114+
}
115+
}
116+
return nil
117+
}
118+
119+
func addrMatch(src, dst *netip.AddrPort, ports []uint16) bool {
120+
var srcPort, dstPort uint16
121+
if src != nil {
122+
srcPort = src.Port()
123+
}
124+
if dst != nil {
125+
dstPort = src.Port()
126+
}
127+
for _, port := range ports {
128+
if srcPort == port || dstPort == port {
129+
return true
130+
}
131+
}
132+
return false
133+
}
134+
135+
func parseNextLayerFromAddress(data []byte, src, dst *netip.AddrPort) Layer {
136+
var next Layer
137+
switch {
138+
case addrMatch(src, dst, []uint16{53, 5353, 853, 5355}):
139+
next = GetNextLayer("DNS")
140+
case addrMatch(src, dst, []uint16{80, 8080, 8000, 8888, 81, 591, 5911}):
141+
next = GetNextLayer("HTTP")
142+
case addrMatch(src, dst, []uint16{161, 162, 10161, 10162, 1161, 2161}):
143+
next = GetNextLayer("SNMP")
144+
case addrMatch(src, dst, []uint16{21, 20, 2121, 8021}):
145+
next = GetNextLayer("FTP")
146+
case addrMatch(src, dst, []uint16{22, 2222, 2200, 222, 2022}):
147+
next = GetNextLayer("SSH")
148+
case addrMatch(src, dst, []uint16{443, 465, 993, 995, 8443, 9443, 10443, 8444}):
149+
next = GetNextLayer("TLS")
150+
default:
151+
return nil
152+
}
153+
if err := next.Parse(data); err == nil {
154+
return next
155+
} else {
156+
return nil
157+
}
158+
}
159+
160+
func ParseNextLayer(data []byte, src, dst *netip.AddrPort) Layer {
161+
buf := make([]byte, 0, len(data))
162+
buf = append(buf, data...)
163+
var next Layer
164+
if src != nil || dst != nil {
165+
if next = parseNextLayerFromAddress(buf, src, dst); next != nil {
166+
return next
167+
}
168+
}
169+
if next = parseNextLayerFromBytes(buf); next != nil {
170+
return next
171+
}
172+
return parseNextLayerFallback(buf)
173+
}
174+
49175
func GetNextLayer(layer string) Layer {
50176
// TODO (shadowy-pycoder): add this to NextLayer, choose by ports, parse, use fallback on error
51177
switch layer {

layers/ssh.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99

1010
const messageSizeSSH = 6
1111

12+
var protoSSH = []byte("SSH-")
13+
1214
type Message struct {
1315
PacketLength uint32
1416
PaddingLength uint8
@@ -87,8 +89,6 @@ func (s *SSHMessage) Parse(data []byte) error {
8789
}
8890
buf := make([]byte, 0, len(data))
8991
buf = append(buf, data...)
90-
s.Protocol = ""
91-
s.Messages = nil
9292
if bytes.HasSuffix(buf, crlf) {
9393
s.Protocol = bytesToStr(bytes.TrimSuffix(buf, crlf))
9494
return nil

0 commit comments

Comments
 (0)