This repository has been archived by the owner on Sep 7, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
hook.moon
69 lines (55 loc) · 2.27 KB
/
hook.moon
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
:concat = table
:inbox = require"mailbox"
:register, family: {BRIDGE: pf}, bridge_hooks: {FORWARD: hooknum}, bridge_priority: {FILTER_BRIDGED: priority}, action: {:CONTINUE, :DROP} = require"netfilter"
:activate, :log_level = require"snihook.config"
DROP = activate and DROP or CONTINUE
:set_log, :notice, :info, :dbg = require"snihook.log"
:auto_ip, IP: {protocols: {TCP: tcp_proto}}, :Fragmented_IP4, :TCP, :TLS, :TLSHandshake, :TLSExtension = require"snihook.ipparse"
:handshake = TLS.types
:hello = TLSHandshake.types
:server_name = TLSExtension.types
whitelist = {}
get_first = (fn) => -- Returns first value of an iterator that matches the condition defined in function fn.
for v in @
return v if fn v
fragmented_ips = setmetatable {}, __mode: "kv", __index: (id) =>
@[id] = Fragmented_IP4!
dbg id, @[id]
@[id]
(dev_queue, log_queue) ->
dev = inbox dev_queue
set_log log_queue, log_level, "snihook"
register :pf, :hooknum, :priority, hook: =>
if changes = dev\receive!
for action, domain in changes\gmatch"(%S+)%s(%S+)"
if action == "add"
whitelist[domain] = true
elseif action == "del"
whitelist[domain] = nil
ip = auto_ip @
return CONTINUE if not ip or ip\is_empty!
if ip\is_fragment!
dbg"Fragment detected"
f_ip = fragmented_ips[ip.id]\insert(ip)
return CONTINUE unless f_ip\is_complete!
dbg"Last fragment received"
ip = f_ip
fragmented_ips[ip.id] = nil
return CONTINUE if ip.protocol ~= tcp_proto
tcp = TCP ip.data
return CONTINUE if tcp\is_empty! or tcp.dport ~= 443
tls = TLS tcp.data
return CONTINUE if tls\is_empty! or tls.type ~= handshake
hshake = TLSHandshake tls.data
return CONTINUE if hshake\is_empty! or hshake.type ~= hello
if sni = get_first hshake\iter_extensions!, => @type == server_name
sni = sni.server_name
if whitelist[sni]
return CONTINUE, info"#{ip.src} -> #{sni} allowed."
sni_parts = [ part for part in sni\gmatch"[^%.]+" ]
for i = 2, #sni_parts
domain = concat [ part for part in *sni_parts[i,] ], "."
if whitelist[domain]
return CONTINUE, info"#{ip.src} -> #{sni} allowed as a subdomain of #{domain}."
return DROP, notice"#{ip.src} -> #{sni} BLOCKED."
CONTINUE