carcal 0.2.0__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- carcal/__init__.py +1 -0
- carcal/__main__.py +41 -0
- carcal/_bundle/carcal.exe +0 -0
- carcal/_bundle/grammars/posa.tmLanguage.json +20 -0
- carcal/_bundle/libcaca-0.dll +0 -0
- carcal/_bundle/libgtcaca.dll +0 -0
- carcal/_bundle/libonig-5.dll +0 -0
- carcal/_bundle/libpcapng.dll +0 -0
- carcal/_bundle/lua51.dll +0 -0
- carcal/_bundle/protos/dhcp.posa +114 -0
- carcal/_bundle/protos/dns.posa +176 -0
- carcal/_bundle/protos/http.posa +59 -0
- carcal/_bundle/protos/igmp.posa +56 -0
- carcal/_bundle/protos/rdp.posa +57 -0
- carcal/_bundle/protos/smb.posa +339 -0
- carcal/_bundle/protos/tftp.posa +52 -0
- carcal/_bundle/protos/tls.posa +136 -0
- carcal/_bundle/scripts/fields.lua +25 -0
- carcal/_bundle/scripts/mysql-queries.lua +28 -0
- carcal/_bundle/scripts/summary.lua +13 -0
- carcal/_bundle/scripts/tftp.lua +21 -0
- carcal/_bundle/zlib1.dll +0 -0
- carcal-0.2.0.dist-info/METADATA +436 -0
- carcal-0.2.0.dist-info/RECORD +27 -0
- carcal-0.2.0.dist-info/WHEEL +4 -0
- carcal-0.2.0.dist-info/entry_points.txt +2 -0
- carcal-0.2.0.dist-info/licenses/LICENSE +21 -0
carcal/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""carcal — a terminal packet analyzer. See `carcal --help`."""
|
carcal/__main__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Entry point for the `carcal` console script."""
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
_BUNDLE = Path(__file__).resolve().parent / "_bundle"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _layout():
|
|
10
|
+
"""(executable, protos, grammars) — the Windows bundle is flat, the others
|
|
11
|
+
are bin/ + lib/ + share/."""
|
|
12
|
+
win = _BUNDLE / "carcal.exe"
|
|
13
|
+
if win.exists():
|
|
14
|
+
return win, _BUNDLE / "protos", _BUNDLE / "grammars"
|
|
15
|
+
share = _BUNDLE / "share" / "carcal"
|
|
16
|
+
return _BUNDLE / "bin" / "carcal", share / "protos", share / "grammars"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main() -> int:
|
|
20
|
+
exe, protos, grammars = _layout()
|
|
21
|
+
if not exe.exists():
|
|
22
|
+
sys.exit(f"carcal: bundled binary is missing from {_BUNDLE} "
|
|
23
|
+
f"(broken install — try reinstalling)")
|
|
24
|
+
|
|
25
|
+
env = dict(os.environ)
|
|
26
|
+
# setdefault, not assignment: an explicit CARCAL_PROTOS_DIR is how a user
|
|
27
|
+
# points carcal at their own .posa decoders, and it must still win.
|
|
28
|
+
env.setdefault("CARCAL_PROTOS_DIR", str(protos))
|
|
29
|
+
env.setdefault("CARCAL_GRAMMARS_DIR", str(grammars))
|
|
30
|
+
|
|
31
|
+
argv = [str(exe), *sys.argv[1:]]
|
|
32
|
+
if os.name == "nt":
|
|
33
|
+
# execve on Windows returns control to the shell immediately, which
|
|
34
|
+
# would hand a live TUI a prompt it is fighting for; spawn and wait.
|
|
35
|
+
import subprocess
|
|
36
|
+
return subprocess.run(argv, env=env).returncode
|
|
37
|
+
os.execve(exe, argv, env) # no return
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
sys.exit(main())
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "posa",
|
|
3
|
+
"scopeName": "source.posa",
|
|
4
|
+
"fileTypes": ["posa"],
|
|
5
|
+
"patterns": [
|
|
6
|
+
{ "name": "comment.line.number-sign.posa", "match": "#.*$" },
|
|
7
|
+
{ "name": "keyword.other.binding.posa", "match": "^\\s*(rule|color)\\b" },
|
|
8
|
+
{ "name": "keyword.control.posa",
|
|
9
|
+
"match": "\\b(Object|protocol|end|default|required|optional|when|else|scope|repeat|until|as|label|bits|seek|layer|include|info|col|abbrev|mask|hex)\\b" },
|
|
10
|
+
{ "name": "storage.type.posa",
|
|
11
|
+
"match": "\\b(uint8|uint16|uint24|uint32|uint64|le_uint16|le_uint32|le_uint64|mac|ip4|ip6|cstring|string|payload|dnsname)\\b" },
|
|
12
|
+
{ "name": "storage.type.posa", "match": "\\b(bytes|str|utf16)(?=[<\\[])" },
|
|
13
|
+
{ "name": "entity.name.type.parent.posa", "match": "<[A-Za-z0-9_]+>" },
|
|
14
|
+
{ "name": "string.quoted.double.posa", "match": "\"(\\\\.|[^\"\\\\])*\"" },
|
|
15
|
+
{ "name": "keyword.operator.posa", "match": "(==|!=|<=|>=|=>|<|>|=|&)" },
|
|
16
|
+
{ "name": "constant.numeric.hex.posa", "match": "\\b0[xX][0-9a-fA-F]+\\b" },
|
|
17
|
+
{ "name": "constant.numeric.posa", "match": "\\b[0-9]+\\b" },
|
|
18
|
+
{ "name": "variable.other.field.posa", "match": "\\b[a-z_][a-z0-9_]*\\b" }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
carcal/_bundle/lua51.dll
ADDED
|
Binary file
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# dhcp.posa — DHCP / BOOTP (RFC 2131, 2132).
|
|
2
|
+
#
|
|
3
|
+
# A fixed BOOTP header, then a magic cookie, then options: a stream of
|
|
4
|
+
# type-length-value records with no count in front of them. That is
|
|
5
|
+
#
|
|
6
|
+
# repeat until end as option records until the packet runs out
|
|
7
|
+
# scope opt_len each option's value is opt_len bytes, and the
|
|
8
|
+
# scope ends there whether we decoded it or not —
|
|
9
|
+
# so an option this file has never heard of still
|
|
10
|
+
# leaves the walk on the right byte
|
|
11
|
+
# when opt_code == 53: the ones worth naming get named…
|
|
12
|
+
# else: …and the rest still show their bytes
|
|
13
|
+
#
|
|
14
|
+
# Pad (0) and End (255) carry no length byte, hence the `when opt_code > 0` /
|
|
15
|
+
# `when opt_code < 255` nesting around the length.
|
|
16
|
+
|
|
17
|
+
Object<main> DHCP
|
|
18
|
+
abbrev "dhcp"
|
|
19
|
+
col "DHCP"
|
|
20
|
+
|
|
21
|
+
required uint8 op "Message Type"
|
|
22
|
+
Boot Request = 1
|
|
23
|
+
Boot Reply = 2
|
|
24
|
+
required uint8 htype "Hardware Type"
|
|
25
|
+
Ethernet = 1
|
|
26
|
+
required uint8 hlen "Hardware Address Length"
|
|
27
|
+
required uint8 hops "Hops"
|
|
28
|
+
required uint32 xid hex "Transaction ID"
|
|
29
|
+
required uint16 secs "Seconds Elapsed"
|
|
30
|
+
required uint16 flags hex "Flags"
|
|
31
|
+
bits flags broadcast 15 1 "Broadcast"
|
|
32
|
+
required ip4 ciaddr "Client IP Address"
|
|
33
|
+
required ip4 yiaddr "Your (client) IP Address"
|
|
34
|
+
required ip4 siaddr "Next Server IP Address"
|
|
35
|
+
required ip4 giaddr "Relay Agent IP Address"
|
|
36
|
+
required mac chaddr "Client MAC Address"
|
|
37
|
+
required bytes<10> chaddr_pad "Client Hardware Address Padding"
|
|
38
|
+
required bytes<64> sname "Server Host Name"
|
|
39
|
+
required bytes<128> file "Boot File Name"
|
|
40
|
+
required uint32 cookie hex "Magic Cookie"
|
|
41
|
+
|
|
42
|
+
repeat until end as option "Options"
|
|
43
|
+
label "%s: %s%s%s" opt_code, msg_type, opt_ip, opt_text
|
|
44
|
+
required uint8 opt_code "Option"
|
|
45
|
+
Pad = 0
|
|
46
|
+
Subnet Mask = 1
|
|
47
|
+
Router = 3
|
|
48
|
+
Domain Name Server = 6
|
|
49
|
+
Host Name = 12
|
|
50
|
+
Domain Name = 15
|
|
51
|
+
Requested IP Address = 50
|
|
52
|
+
IP Address Lease Time = 51
|
|
53
|
+
DHCP Message Type = 53
|
|
54
|
+
Server Identifier = 54
|
|
55
|
+
Parameter Request List = 55
|
|
56
|
+
Maximum DHCP Message Size = 57
|
|
57
|
+
Renewal Time Value = 58
|
|
58
|
+
Rebinding Time Value = 59
|
|
59
|
+
Vendor Class Identifier = 60
|
|
60
|
+
Client Identifier = 61
|
|
61
|
+
End = 255
|
|
62
|
+
when opt_code > 0:
|
|
63
|
+
when opt_code < 255:
|
|
64
|
+
required uint8 opt_len "Length"
|
|
65
|
+
scope opt_len
|
|
66
|
+
when opt_code == 53:
|
|
67
|
+
required uint8 msg_type "DHCP Message Type"
|
|
68
|
+
Discover = 1
|
|
69
|
+
Offer = 2
|
|
70
|
+
Request = 3
|
|
71
|
+
Decline = 4
|
|
72
|
+
ACK = 5
|
|
73
|
+
NAK = 6
|
|
74
|
+
Release = 7
|
|
75
|
+
Inform = 8
|
|
76
|
+
when opt_code == 1:
|
|
77
|
+
required ip4 opt_ip "Subnet Mask"
|
|
78
|
+
when opt_code == 3:
|
|
79
|
+
required ip4 opt_ip "Router"
|
|
80
|
+
when opt_code == 6:
|
|
81
|
+
repeat until end as dns_server
|
|
82
|
+
label "DNS Server: %s" opt_ip
|
|
83
|
+
required ip4 opt_ip "Domain Name Server"
|
|
84
|
+
when opt_code == 50:
|
|
85
|
+
required ip4 opt_ip "Requested IP Address"
|
|
86
|
+
when opt_code == 54:
|
|
87
|
+
required ip4 opt_ip "Server Identifier"
|
|
88
|
+
when opt_code == 51:
|
|
89
|
+
required uint32 lease_time "IP Address Lease Time"
|
|
90
|
+
when opt_code == 12:
|
|
91
|
+
required str[opt_len] opt_text "Host Name"
|
|
92
|
+
when opt_code == 15:
|
|
93
|
+
required str[opt_len] opt_text "Domain Name"
|
|
94
|
+
when opt_code == 60:
|
|
95
|
+
required str[opt_len] opt_text "Vendor Class Identifier"
|
|
96
|
+
when opt_code == 55:
|
|
97
|
+
repeat until end as requested
|
|
98
|
+
label "Requested: %s" param
|
|
99
|
+
required uint8 param "Parameter"
|
|
100
|
+
Subnet Mask = 1
|
|
101
|
+
Router = 3
|
|
102
|
+
Domain Name Server = 6
|
|
103
|
+
Host Name = 12
|
|
104
|
+
Domain Name = 15
|
|
105
|
+
Broadcast Address = 28
|
|
106
|
+
else:
|
|
107
|
+
required payload opt_value "Value"
|
|
108
|
+
|
|
109
|
+
info "DHCP %s - Transaction ID 0x%x" msg_type, xid
|
|
110
|
+
|
|
111
|
+
rule udp.port == 67 => DHCP
|
|
112
|
+
rule udp.port == 68 => DHCP
|
|
113
|
+
|
|
114
|
+
color dhcp => black lightblue
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# dns.posa — DNS / mDNS / LLMNR (RFC 1035, RFC 6762), the whole message.
|
|
2
|
+
#
|
|
3
|
+
# This is the decoder carcal uses for ports 53, 5353 and 5355 — there is no C
|
|
4
|
+
# dissector behind it. Read it as the spec: a header, then four sections of
|
|
5
|
+
# records, each section repeated as many times as its counter says.
|
|
6
|
+
#
|
|
7
|
+
# The pieces of posa that make a record protocol expressible:
|
|
8
|
+
#
|
|
9
|
+
# repeat <count> as <item> one subtree per record, count taken from a field
|
|
10
|
+
# repeat until end as <item> records until the enclosing scope runs out
|
|
11
|
+
# label "..." a, b how to title a record's subtree, from its fields
|
|
12
|
+
# dnsname a name, following 0xc0 compression pointers
|
|
13
|
+
# bits <src> <n> <sh> <w> a bitfield carved out of an already-parsed field
|
|
14
|
+
# scope <lenfield> bound the next fields to <lenfield> bytes (rdata)
|
|
15
|
+
# include <Object> inline another object's fields (the RR body,
|
|
16
|
+
# shared by the answer/authority/additional sections)
|
|
17
|
+
#
|
|
18
|
+
# To change what you see: edit this file (Analyze ▸ Decoders ▸ DNS ▸ Enter) and
|
|
19
|
+
# save — no rebuild. Add an RR type by adding a `when type == N:` block.
|
|
20
|
+
|
|
21
|
+
# ── A resource record ────────────────────────────────────────────────────────
|
|
22
|
+
# Name, type, class, TTL, then rdlength bytes of type-dependent data. `scope
|
|
23
|
+
# rdlength` bounds the rdata so an unknown type still leaves the walk on the
|
|
24
|
+
# right byte — the scope ends where rdlength says, decoded or not.
|
|
25
|
+
#
|
|
26
|
+
# Defined first because the three record sections below `include` it.
|
|
27
|
+
Object<main> RR
|
|
28
|
+
abbrev "dns"
|
|
29
|
+
|
|
30
|
+
required dnsname name "Name"
|
|
31
|
+
required uint16 type "Type"
|
|
32
|
+
A = 1
|
|
33
|
+
NS = 2
|
|
34
|
+
CNAME = 5
|
|
35
|
+
SOA = 6
|
|
36
|
+
PTR = 12
|
|
37
|
+
HINFO = 13
|
|
38
|
+
MX = 15
|
|
39
|
+
TXT = 16
|
|
40
|
+
AAAA = 28
|
|
41
|
+
SRV = 33
|
|
42
|
+
OPT = 41
|
|
43
|
+
NSEC = 47
|
|
44
|
+
HTTPS = 65
|
|
45
|
+
required uint16 class mask 0x7fff "Class"
|
|
46
|
+
IN = 1
|
|
47
|
+
CH = 3
|
|
48
|
+
ANY = 255
|
|
49
|
+
# mDNS reuses the top class bit to mean "flush your cache for this name"
|
|
50
|
+
bits class flush 15 1 "Cache flush"
|
|
51
|
+
required uint32 ttl "Time to live"
|
|
52
|
+
required uint16 rdlength "Data length"
|
|
53
|
+
|
|
54
|
+
scope rdlength
|
|
55
|
+
when type == 1:
|
|
56
|
+
required ip4 rdata "Address"
|
|
57
|
+
when type == 28:
|
|
58
|
+
required ip6 rdata "Address"
|
|
59
|
+
when type == 12:
|
|
60
|
+
required dnsname rdata "Domain name"
|
|
61
|
+
when type == 5:
|
|
62
|
+
required dnsname rdata "CNAME"
|
|
63
|
+
when type == 2:
|
|
64
|
+
required dnsname rdata "Name server"
|
|
65
|
+
when type == 15:
|
|
66
|
+
required uint16 preference "Preference"
|
|
67
|
+
required dnsname rdata "Mail exchange"
|
|
68
|
+
when type == 33:
|
|
69
|
+
required uint16 priority "Priority"
|
|
70
|
+
required uint16 weight "Weight"
|
|
71
|
+
required uint16 port "Port"
|
|
72
|
+
required dnsname rdata "Target"
|
|
73
|
+
when type == 6:
|
|
74
|
+
required dnsname rdata "Primary name server"
|
|
75
|
+
required dnsname rname "Responsible mailbox"
|
|
76
|
+
required uint32 serial "Serial"
|
|
77
|
+
required uint32 refresh "Refresh"
|
|
78
|
+
required uint32 retry "Retry"
|
|
79
|
+
required uint32 expire "Expire"
|
|
80
|
+
required uint32 minimum "Minimum TTL"
|
|
81
|
+
# TXT is a run of length-prefixed strings — "model=J96AP", "osxvers=21", …
|
|
82
|
+
# There is no count for them, so repeat until the rdata scope runs out.
|
|
83
|
+
when type == 16:
|
|
84
|
+
repeat until end as txt
|
|
85
|
+
label "%s" rdata
|
|
86
|
+
required uint8 txtlen "Length"
|
|
87
|
+
required str[txtlen] rdata "Text"
|
|
88
|
+
when type == 41:
|
|
89
|
+
required payload rdata "Options"
|
|
90
|
+
when type == 47:
|
|
91
|
+
required dnsname rdata "Next domain name"
|
|
92
|
+
|
|
93
|
+
Object<main> DNS
|
|
94
|
+
abbrev "dns"
|
|
95
|
+
col "DNS"
|
|
96
|
+
|
|
97
|
+
required uint16 id "Transaction ID"
|
|
98
|
+
required uint16 flags "Flags"
|
|
99
|
+
bits flags qr 15 1 "Response"
|
|
100
|
+
Query = 0
|
|
101
|
+
Response = 1
|
|
102
|
+
bits flags opcode 11 4 "Opcode"
|
|
103
|
+
QUERY = 0
|
|
104
|
+
IQUERY = 1
|
|
105
|
+
STATUS = 2
|
|
106
|
+
NOTIFY = 4
|
|
107
|
+
UPDATE = 5
|
|
108
|
+
bits flags aa 10 1 "Authoritative"
|
|
109
|
+
bits flags tc 9 1 "Truncated"
|
|
110
|
+
bits flags rd 8 1 "Recursion desired"
|
|
111
|
+
bits flags ra 7 1 "Recursion available"
|
|
112
|
+
bits flags rcode 0 4 "Reply code"
|
|
113
|
+
NoError = 0
|
|
114
|
+
FormErr = 1
|
|
115
|
+
ServFail = 2
|
|
116
|
+
NXDomain = 3
|
|
117
|
+
NotImp = 4
|
|
118
|
+
Refused = 5
|
|
119
|
+
required uint16 questions "Questions"
|
|
120
|
+
required uint16 answers "Answer RRs"
|
|
121
|
+
required uint16 authority "Authority RRs"
|
|
122
|
+
required uint16 additional "Additional RRs"
|
|
123
|
+
|
|
124
|
+
# ── Queries ───────────────────────────────────────────────────────────────
|
|
125
|
+
# A question is a name, a type, and a class. In mDNS the top class bit is the
|
|
126
|
+
# "QU" bit (send the reply unicast), so the class is masked and the bit shown
|
|
127
|
+
# on its own — that is what `mask` and `bits` are for.
|
|
128
|
+
repeat questions as query "Queries"
|
|
129
|
+
label "%s: type %s, class %s" qname, qtype, qclass
|
|
130
|
+
required dnsname qname "Name"
|
|
131
|
+
required uint16 qtype "Type"
|
|
132
|
+
A = 1
|
|
133
|
+
NS = 2
|
|
134
|
+
CNAME = 5
|
|
135
|
+
SOA = 6
|
|
136
|
+
PTR = 12
|
|
137
|
+
HINFO = 13
|
|
138
|
+
MX = 15
|
|
139
|
+
TXT = 16
|
|
140
|
+
AAAA = 28
|
|
141
|
+
SRV = 33
|
|
142
|
+
OPT = 41
|
|
143
|
+
NSEC = 47
|
|
144
|
+
HTTPS = 65
|
|
145
|
+
ANY = 255
|
|
146
|
+
required uint16 qclass mask 0x7fff "Class"
|
|
147
|
+
IN = 1
|
|
148
|
+
CH = 3
|
|
149
|
+
NONE = 254
|
|
150
|
+
ANY = 255
|
|
151
|
+
bits qclass qu 15 1 "QU (reply wanted unicast)"
|
|
152
|
+
Multicast = 0
|
|
153
|
+
Unicast = 1
|
|
154
|
+
|
|
155
|
+
repeat answers as answer "Answers"
|
|
156
|
+
label "%s: type %s, class %s, %s" name, type, class, rdata
|
|
157
|
+
include RR
|
|
158
|
+
|
|
159
|
+
repeat authority as authority_rr "Authoritative nameservers"
|
|
160
|
+
label "%s: type %s, class %s, %s" name, type, class, rdata
|
|
161
|
+
include RR
|
|
162
|
+
|
|
163
|
+
repeat additional as additional_rr "Additional records"
|
|
164
|
+
label "%s: type %s, class %s, %s" name, type, class, rdata
|
|
165
|
+
include RR
|
|
166
|
+
|
|
167
|
+
# A query has questions and no records; a response is usually the reverse —
|
|
168
|
+
# name both, and the fields the packet does not carry expand to nothing.
|
|
169
|
+
info "%s 0x%x %s %s %s %s" qr, id, qtype, qname, type, rdata
|
|
170
|
+
|
|
171
|
+
rule udp.port == 53 => DNS
|
|
172
|
+
rule udp.port == 5353 => DNS
|
|
173
|
+
rule udp.port == 5355 => DNS
|
|
174
|
+
|
|
175
|
+
color dns.rcode > 0 => yellow red
|
|
176
|
+
color dns => white blue
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# http.posa — HTTP/1.x (RFC 9112).
|
|
2
|
+
#
|
|
3
|
+
# HTTP has no lengths in front of anything: it is lines of text ending in CRLF,
|
|
4
|
+
# then a blank line, then the body. Two posa constructs cover that shape:
|
|
5
|
+
#
|
|
6
|
+
# string <name> until "<delim>" one delimited token
|
|
7
|
+
# repeat until "<delim>" as <item> records until those bytes come next —
|
|
8
|
+
# here, header lines until the blank line
|
|
9
|
+
#
|
|
10
|
+
# A request and a response differ only in their first line, so they are two
|
|
11
|
+
# objects in an Object<HTTP> group, told apart by their first field: a response
|
|
12
|
+
# starts with "HTTP", a request with a method. The group is dispatched on the
|
|
13
|
+
# first field's value, so "HTTP" as a uint32 (0x48545450) selects the response.
|
|
14
|
+
#
|
|
15
|
+
# Not covered (add it here, no rebuild): chunked transfer-encoding is shown as
|
|
16
|
+
# the raw body, and the body is not decompressed.
|
|
17
|
+
|
|
18
|
+
Object<HTTP> HTTP_RESPONSE
|
|
19
|
+
abbrev "http"
|
|
20
|
+
col "HTTP"
|
|
21
|
+
|
|
22
|
+
# the group is dispatched on the first field's value, so a response is
|
|
23
|
+
# recognised by its "HTTP" magic — then `seek 0` rewinds over it, and the
|
|
24
|
+
# status line is read as the text it actually is
|
|
25
|
+
required uint32 magic = 0x48545450 hex "Magic"
|
|
26
|
+
seek 0
|
|
27
|
+
required string version until " " "Version"
|
|
28
|
+
required string status_code until " " "Status Code"
|
|
29
|
+
required string reason until "\r\n" "Reason Phrase"
|
|
30
|
+
repeat until "\r\n" as header "Headers"
|
|
31
|
+
label "%s" line
|
|
32
|
+
required string line until "\r\n" "Header"
|
|
33
|
+
required string blank until "\r\n" "End of headers"
|
|
34
|
+
required payload body "Body"
|
|
35
|
+
|
|
36
|
+
info "%s %s" status_code, reason
|
|
37
|
+
|
|
38
|
+
Object<HTTP> HTTP_REQUEST default
|
|
39
|
+
abbrev "http"
|
|
40
|
+
col "HTTP"
|
|
41
|
+
|
|
42
|
+
required string method until " " "Method"
|
|
43
|
+
required string uri until " " "Request URI"
|
|
44
|
+
required string version until "\r\n" "Version"
|
|
45
|
+
repeat until "\r\n" as header "Headers"
|
|
46
|
+
label "%s" line
|
|
47
|
+
required string line until "\r\n" "Header"
|
|
48
|
+
required string blank until "\r\n" "End of headers"
|
|
49
|
+
required payload body "Body"
|
|
50
|
+
|
|
51
|
+
info "%s %s %s" method, uri, version
|
|
52
|
+
|
|
53
|
+
rule tcp.port == 80 => HTTP
|
|
54
|
+
rule tcp.port == 8080 => HTTP
|
|
55
|
+
rule tcp.port == 8000 => HTTP
|
|
56
|
+
rule tcp.port == 8888 => HTTP
|
|
57
|
+
rule tcp.port == 3128 => HTTP
|
|
58
|
+
|
|
59
|
+
color http => black lightgray
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# igmp.posa — IGMP v1/v2/v3 (RFC 1112, 2236, 3376).
|
|
2
|
+
#
|
|
3
|
+
# Bound by IP protocol number, not a port: rule ip.proto == 2 => IGMP
|
|
4
|
+
#
|
|
5
|
+
# The v3 membership report (type 0x22) is a different shape from the queries and
|
|
6
|
+
# the v1/v2 reports: no group address in the header, just a count of the group
|
|
7
|
+
# records that follow, each with its own list of source addresses. That is two
|
|
8
|
+
# nested `repeat`s — the outer one counted by a header field, the inner by a
|
|
9
|
+
# field of the record it is inside.
|
|
10
|
+
#
|
|
11
|
+
# Every field a posa decoder produces carries its byte range, so selecting a row
|
|
12
|
+
# in the details pane highlights the right bytes in the hex pane. That is free
|
|
13
|
+
# here; in hand-written C it is a call you can forget to make.
|
|
14
|
+
|
|
15
|
+
Object<main> IGMP
|
|
16
|
+
abbrev "igmp"
|
|
17
|
+
col "IGMP"
|
|
18
|
+
|
|
19
|
+
required uint8 type "Type"
|
|
20
|
+
Membership Query = 0x11
|
|
21
|
+
IGMPv1 Membership Report = 0x12
|
|
22
|
+
IGMPv2 Membership Report = 0x16
|
|
23
|
+
Leave Group = 0x17
|
|
24
|
+
IGMPv3 Membership Report = 0x22
|
|
25
|
+
required uint8 max_resp "Max Resp Time"
|
|
26
|
+
required uint16 checksum "Checksum"
|
|
27
|
+
|
|
28
|
+
# v3 membership report: reserved, then N group records
|
|
29
|
+
when type == 0x22:
|
|
30
|
+
required uint16 reserved "Reserved"
|
|
31
|
+
required uint16 num_records "Num Group Records"
|
|
32
|
+
repeat num_records as record "Group Records"
|
|
33
|
+
label "%s: %s (%u source(s))" record_type, maddr, num_sources
|
|
34
|
+
required uint8 record_type "Record Type"
|
|
35
|
+
MODE_IS_INCLUDE = 1
|
|
36
|
+
MODE_IS_EXCLUDE = 2
|
|
37
|
+
CHANGE_TO_INCLUDE = 3
|
|
38
|
+
CHANGE_TO_EXCLUDE = 4
|
|
39
|
+
ALLOW_NEW_SOURCES = 5
|
|
40
|
+
BLOCK_OLD_SOURCES = 6
|
|
41
|
+
required uint8 aux_len "Aux Data Length"
|
|
42
|
+
required uint16 num_sources "Num Sources"
|
|
43
|
+
required ip4 maddr "Multicast Address"
|
|
44
|
+
repeat num_sources as source "Source Addresses"
|
|
45
|
+
label "Source: %s" saddr
|
|
46
|
+
required ip4 saddr "Source Address"
|
|
47
|
+
|
|
48
|
+
# everything else (queries, v1/v2 reports, leave) carries a group address
|
|
49
|
+
when type != 0x22:
|
|
50
|
+
required ip4 maddr "Multicast Address"
|
|
51
|
+
|
|
52
|
+
info "%s %s" type, maddr
|
|
53
|
+
|
|
54
|
+
rule ip.proto == 2 => IGMP
|
|
55
|
+
|
|
56
|
+
color igmp => black lightgreen
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# rdp.posa — Remote Desktop Protocol over TPKT / ISO-8073 (COTP), fully in posa.
|
|
2
|
+
# Demonstrates the extended grammar: layered sub-protocols, length scopes,
|
|
3
|
+
# conditional layout, a delimiter field, a derived Info string, and a port rule.
|
|
4
|
+
|
|
5
|
+
protocol TPKT
|
|
6
|
+
col "RDP"
|
|
7
|
+
abbrev "rdp"
|
|
8
|
+
required uint8 version
|
|
9
|
+
required uint8 reserved
|
|
10
|
+
required uint16 length
|
|
11
|
+
layer cotp COTP
|
|
12
|
+
|
|
13
|
+
protocol COTP
|
|
14
|
+
abbrev "cotp"
|
|
15
|
+
required uint8 li
|
|
16
|
+
required uint8 pdu_type
|
|
17
|
+
CR = 0xe0
|
|
18
|
+
CC = 0xd0
|
|
19
|
+
DT = 0xf0
|
|
20
|
+
scope li
|
|
21
|
+
when pdu_type & 0xf0 == 0xe0:
|
|
22
|
+
required uint16 dst_ref
|
|
23
|
+
required uint16 src_ref
|
|
24
|
+
required uint8 class
|
|
25
|
+
layer neg RDP_NEGOTIATION
|
|
26
|
+
when pdu_type & 0xf0 == 0xd0:
|
|
27
|
+
required uint16 dst_ref
|
|
28
|
+
required uint16 src_ref
|
|
29
|
+
required uint8 class
|
|
30
|
+
layer neg RDP_NEGOTIATION
|
|
31
|
+
when pdu_type & 0xf0 == 0xf0:
|
|
32
|
+
required uint8 eot
|
|
33
|
+
info "COTP %s" pdu_type
|
|
34
|
+
|
|
35
|
+
protocol RDP_NEGOTIATION
|
|
36
|
+
abbrev "rdp"
|
|
37
|
+
optional string cookie until "\r\n"
|
|
38
|
+
when remaining >= 8:
|
|
39
|
+
required uint8 type
|
|
40
|
+
Request = 1
|
|
41
|
+
Response = 2
|
|
42
|
+
Failure = 3
|
|
43
|
+
required uint8 flags
|
|
44
|
+
required le_uint16 length
|
|
45
|
+
required le_uint32 protocols
|
|
46
|
+
TLS = 0x1
|
|
47
|
+
CredSSP = 0x3
|
|
48
|
+
info "%s Negotiate %s" cookie, type
|
|
49
|
+
|
|
50
|
+
rule tcp.port == 3389 => TPKT
|
|
51
|
+
|
|
52
|
+
# Coloring — carcal paints a matching packet with these colors, first match wins.
|
|
53
|
+
# Declared here so the decoder ships its own look: drop this file into protos/
|
|
54
|
+
# and RDP is both decoded and colored, with no rebuild. Consulted before
|
|
55
|
+
# carcal's generic tcp rule, so they win over it.
|
|
56
|
+
color rdp.type == 3 => yellow red # negotiation Failure
|
|
57
|
+
color rdp => black lightcyan
|