forked from threatgrid/ctim
/
ip.clj
105 lines (90 loc) · 3.39 KB
/
ip.clj
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
(ns ctim.domain.observables.ip
(:require [clojure.string :as string]
[schema.core :as s]
[clojure.network.ip :refer [make-network]]))
(def ipv4-regex #"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")
(def ipv6-regex #"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))")
(def private-ipv4-masks
["10.0.0.0/8"
"172.16.0.0/12"
"192.168.0.0/16"])
(def private-ipv6-mask "fc00::/7")
(def special-ipv4-masks
;;https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
(concat private-ipv4-masks
["0.0.0.0/8"
"100.64.0.0/10"
"127.0.0.0/8"
"169.254.0.0/16"
"192.0.0.0/24"
"192.0.2.0/24"
"192.88.99.0/24"
"198.18.0.0/15"
"198.51.100.0/24"
"203.0.113.0/24"
"224.0.0.0/4"
"240.0.0.0/4"
"255.255.255.255/32"]))
(def special-ipv6-masks
;;https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
[private-ipv6-mask
"::1/128"
"::/128"
"100::/64"
"2001::/23"
"2001::/32"
"2001:1::1/128"
"2001:1::2/128"
"2001:2::/48"
"2001:3::/32"
"2001:4:112::/48"
"2001:5::/32"
"2001:10::/28"
"2001:20::/28"
"2001:db8::/32"
"2002::/16"
"2620:4f:8000::/48"
"fe80::/10"])
(s/defn valid-ipv4? :- s/Bool
"Is this a valid ipv4 address?"
[ip-str :- s/Str]
(when ip-str
(some? (re-matches ipv4-regex ip-str))))
(s/defn valid-ipv6? :- s/Bool
"Is this a valid ipv6 address?"
[ip-str :- s/Str]
(when ip-str
(some? (re-matches ipv6-regex ip-str))))
(s/defn valid-ip? :- s/Bool
"Is this a valid ipv4 or ipv6 address?"
[ip-str :- s/Str]
(or (valid-ipv4? ip-str)
(valid-ipv6? ip-str)))
(s/defn match-mask? :- s/Bool
"Does this ip match given mask?"
[ip-str :- s/Str
mask :- s/Str]
(and (valid-ip? ip-str)
(-> (make-network mask)
(contains? ip-str))))
(s/defn match-some-masks? :- s/Bool
"Does this ip match one of given masks?"
[ip-str :- s/Str
masks :- [s/Str]]
(and (valid-ip? ip-str)
(true? (some (partial match-mask? ip-str) masks))))
(s/defn special-ip? :- s/Bool
"Is this IP within a special block of IPs?"
[ip-str :- s/Str]
(match-some-masks? ip-str
(concat special-ipv4-masks special-ipv6-masks)))
(s/defn private-ip? :- s/Bool
"Is this a IP reserved for private network usage"
[ip-str :- s/Str]
(match-some-masks? ip-str (cons private-ipv6-mask private-ipv4-masks)))
(s/defn normalize-ip :- (s/maybe s/Str)
"Normalizes an ip that was modified with a known transformation"
[ip-str :- s/Str]
(let [normal-ip-form-str (string/replace ip-str #"\[\.\]" ".")]
(when (valid-ip? normal-ip-form-str)
normal-ip-form-str)))