RT-AC66 3.0.0.4.374.130 core
[tomato.git] / release / src-rt-6.x / linux / linux-2.6 / net / netfilter / xt_web.c
blob842ab8dfebf257e17a636e37b2b0453f4f3ea3f5
1 /*
3 web (experimental)
4 HTTP client request match
5 Copyright (C) 2006 Jonathan Zarate
7 Linux Kernel 2.6 Port (ipt->xt)
8 Portions copyright (C) 2010 Fedor Kozhevnikov
10 Licensed under GNU GPL v2 or later.
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
15 #include <linux/version.h>
16 #include <linux/ip.h>
17 #include <linux/ipv6.h>
18 #include <linux/tcp.h>
19 #include <net/ipv6.h>
20 #include <net/sock.h>
22 #include <linux/netfilter/x_tables.h>
23 #include <linux/netfilter/xt_web.h>
24 #include <linux/netfilter_ipv4/ip_tables.h>
25 #include <linux/netfilter_ipv6/ip6_tables.h>
27 MODULE_AUTHOR("Jonathan Zarate");
28 MODULE_DESCRIPTION("Xtables: HTTP client request match (experimental)");
29 MODULE_LICENSE("GPL");
30 MODULE_ALIAS("ipt_web");
31 MODULE_ALIAS("ip6t_web");
33 //#define DEBUG
35 #ifdef DEBUG
36 #define LOG printk
37 #else
38 #define LOG(...) do { } while (0);
39 #endif
41 #define MAX_PAYLOAD_LEN 2048
43 static int find(const char *data, const char *tail, const char *text)
45 int n, o;
46 int dlen;
47 const char *p, *e;
49 while ((data < tail) && (*data == ' ')) ++data;
50 while ((tail > data) && (*(tail - 1) == ' ')) --tail;
52 dlen = tail - data;
54 #ifdef DEBUG
56 char tmp[128];
57 int z;
58 z = sizeof(tmp) - 1;
59 if (z > dlen) z = dlen;
60 memcpy(tmp, data, z);
61 tmp[z] = 0;
62 LOG(KERN_INFO "find in '%s'\n", tmp);
64 #endif
66 // 012345
67 // text
68 // ^text
69 // text$
70 // ^text$
71 // 012345
73 while (*text) {
74 n = o = strlen(text);
75 if (*text == '^') {
76 --n;
77 if (*(text + n) == '$') {
78 // exact
79 --n;
80 if ((dlen == n) && (memcmp(data, text + 1, n) == 0)) {
81 LOG(KERN_INFO "matched %s\n", text);
82 return 1;
85 else {
86 // begins with
87 if ((dlen >= n) && (memcmp(data, text + 1, n) == 0)) {
88 LOG(KERN_INFO "matched %s\n", text);
89 return 1;
93 else if (*(text + n - 1) == '$') {
94 // ends with
95 --n;
96 if (memcmp(tail - n, text, n) == 0) {
97 LOG(KERN_INFO "matched %s\n", text);
98 return 1;
101 else {
102 // contains
103 p = data;
104 e = tail - n;
105 while (p <= e) {
106 if (memcmp(p, text, n) == 0) {
107 LOG(KERN_INFO "matched %s\n", text);
108 return 1;
110 ++p;
114 text += o + 1;
116 return 0;
119 static inline const char *findend(const char *data, const char *tail, int min)
121 int n = tail - data;
122 if (n >= min) {
123 while (data < tail) {
124 if (*data == '\r') return data;
125 ++data;
128 return NULL;
131 static int match_payload(const struct xt_web_info *info, const char *data, int dlen)
133 const char *tail;
134 const char *p, *q;
135 __u32 sig;
136 #ifdef DEBUG
137 char tmp[64];
138 #endif
140 // POST / HTTP/1.0$$$$
141 // GET / HTTP/1.0$$$$
142 // 1234567890123456789
143 if (dlen < 18) return info->invert;
145 #ifdef DEBUG
146 printk(KERN_INFO "dlen=%d\n", dlen);
147 memcpy(tmp, data, min(63, dlen));
148 tmp[min(63, dlen)] = 0;
149 printk(KERN_INFO "[%s]\n", tmp);
150 #endif
152 // "GET " or "POST"
153 sig = *(__u32 *)data;
154 if ((sig != __constant_htonl(0x47455420)) && (sig != __constant_htonl(0x504f5354))) {
155 return info->invert;
158 tail = data + min(dlen, MAX_PAYLOAD_LEN);
160 // POST / HTTP/1.0$$$$
161 // GET / HTTP/1.0$$$$ -- minimum
162 // 0123456789012345678
163 // 9876543210
164 if (((p = findend(data + 14, tail, 18)) == NULL) || (memcmp(p - 9, " HTTP/", 6) != 0))
165 return info->invert;
167 #ifdef DEBUG
169 const char *qq = info->text;
170 while (*qq) {
171 printk(KERN_INFO "text=%s\n", qq);
172 qq += strlen(qq) + 1;
175 #endif
177 switch (info->mode) {
178 case XT_WEB_HTTP:
179 return !info->invert;
180 case XT_WEB_HORE:
181 // entire request line, else host line
182 if (find(data + 4, p - 9, info->text)) return !info->invert;
183 break;
184 case XT_WEB_PATH:
185 // left side of '?' or entire line
186 q = data += 4;
187 p -= 9;
188 while ((q < p) && (*q != '?')) ++q;
189 return find(data, q, info->text) ^ info->invert;
190 case XT_WEB_QUERY:
191 // right side of '?' or none
192 q = data + 4;
193 p -= 9;
194 while ((q < p) && (*q != '?')) ++q;
195 if (q >= p) return info->invert;
196 return find(q + 1, p, info->text) ^ info->invert;
197 case XT_WEB_RURI:
198 // entire request line
199 return find(data + 4, p - 9, info->text) ^ info->invert;
200 default:
201 // shutup compiler
202 break;
205 // else, XT_WEB_HOST
207 while (1) {
208 data = p + 2; // skip previous \r\n
209 p = findend(data, tail, 8); // p = current line's \r
210 if (p == NULL) return 0;
212 #ifdef DEBUG
213 memcpy(tmp, data, 32);
214 tmp[32] = 0;
215 printk(KERN_INFO "data=[%s]\n", tmp);
216 #endif
218 if (memcmp(data, "Host: ", 6) == 0)
219 return find(data + 6, p, info->text) ^ info->invert;
222 return !info->invert;
225 static int match(const struct sk_buff *skbin,
226 const struct net_device *in,
227 const struct net_device *out,
228 const struct xt_match *match,
229 const void *matchinfo,
230 int offset,
231 unsigned int protoff,
232 int *hotdrop)
234 /* sidestep const without getting a compiler warning... */
235 struct sk_buff *skb = (struct sk_buff *)skbin;
237 const struct xt_web_info *info = matchinfo;
238 struct tcphdr *tcph;
239 const char *data;
240 int len;
242 if (offset != 0) return info->invert;
244 if (skb_is_nonlinear(skb)) {
245 if (unlikely(skb_linearize(skb))) {
246 // failed to linearize packet, bailing
247 return info->invert;
251 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
252 if (match->family == AF_INET6)
254 const struct ipv6hdr *iph = ipv6_hdr(skb);
255 u8 nexthdr = iph->nexthdr;
257 tcph = (void *)iph + ipv6_skip_exthdr(skb, sizeof(*iph), &nexthdr);
258 data = (void *)tcph + (tcph->doff * 4);
259 len = ntohs(iph->payload_len) - (data - (char *)tcph);
261 else
262 #endif
264 const struct iphdr *iph = ip_hdr(skb);
266 tcph = (void *)iph + (iph->ihl * 4);
267 data = (void *)tcph + (tcph->doff * 4);
268 len = ntohs(iph->tot_len) - (data - (char *)iph);
271 return match_payload(info, data, len);
274 static struct xt_match web_match[] = {
276 .name = "web",
277 .family = AF_INET,
278 .match = match,
279 .matchsize = sizeof(struct xt_web_info),
280 .proto = IPPROTO_TCP,
281 .me = THIS_MODULE,
283 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
285 .name = "web",
286 .family = AF_INET6,
287 .match = match,
288 .matchsize = sizeof(struct xt_web_info),
289 .proto = IPPROTO_TCP,
290 .me = THIS_MODULE,
292 #endif
295 static int __init init(void)
297 LOG(KERN_INFO "xt_web <" __DATE__ " " __TIME__ "> loaded\n");
298 return xt_register_matches(web_match, ARRAY_SIZE(web_match));
301 static void __exit fini(void)
303 xt_unregister_matches(web_match, ARRAY_SIZE(web_match));
306 module_init(init);
307 module_exit(fini);