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>
17 #include <linux/ipv6.h>
18 #include <linux/tcp.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");
38 #define LOG(...) do { } while (0);
41 #define MAX_PAYLOAD_LEN 2048
43 static int find(const char *data
, const char *tail
, const char *text
)
49 while ((data
< tail
) && (*data
== ' ')) ++data
;
50 while ((tail
> data
) && (*(tail
- 1) == ' ')) --tail
;
59 if (z
> dlen
) z
= dlen
;
62 LOG(KERN_INFO
"find in '%s'\n", tmp
);
77 if (*(text
+ n
) == '$') {
80 if ((dlen
== n
) && (memcmp(data
, text
+ 1, n
) == 0)) {
81 LOG(KERN_INFO
"matched %s\n", text
);
87 if ((dlen
>= n
) && (memcmp(data
, text
+ 1, n
) == 0)) {
88 LOG(KERN_INFO
"matched %s\n", text
);
93 else if (*(text
+ n
- 1) == '$') {
96 if (memcmp(tail
- n
, text
, n
) == 0) {
97 LOG(KERN_INFO
"matched %s\n", text
);
106 if (memcmp(p
, text
, n
) == 0) {
107 LOG(KERN_INFO
"matched %s\n", text
);
119 static inline const char *findend(const char *data
, const char *tail
, int min
)
123 while (data
< tail
) {
124 if (*data
== '\r') return data
;
131 static int match_payload(const struct xt_web_info
*info
, const char *data
, int dlen
)
140 // POST / HTTP/1.0$$$$
141 // GET / HTTP/1.0$$$$
142 // 1234567890123456789
143 if (dlen
< 18) return info
->invert
;
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
);
153 sig
= *(__u32
*)data
;
154 if ((sig
!= __constant_htonl(0x47455420)) && (sig
!= __constant_htonl(0x504f5354))) {
158 tail
= data
+ min(dlen
, MAX_PAYLOAD_LEN
);
160 // POST / HTTP/1.0$$$$
161 // GET / HTTP/1.0$$$$ -- minimum
162 // 0123456789012345678
164 if (((p
= findend(data
+ 14, tail
, 18)) == NULL
) || (memcmp(p
- 9, " HTTP/", 6) != 0))
169 const char *qq
= info
->text
;
171 printk(KERN_INFO
"text=%s\n", qq
);
172 qq
+= strlen(qq
) + 1;
177 switch (info
->mode
) {
179 return !info
->invert
;
181 // entire request line, else host line
182 if (find(data
+ 4, p
- 9, info
->text
)) return !info
->invert
;
185 // left side of '?' or entire line
188 while ((q
< p
) && (*q
!= '?')) ++q
;
189 return find(data
, q
, info
->text
) ^ info
->invert
;
191 // right side of '?' or none
194 while ((q
< p
) && (*q
!= '?')) ++q
;
195 if (q
>= p
) return info
->invert
;
196 return find(q
+ 1, p
, info
->text
) ^ info
->invert
;
198 // entire request line
199 return find(data
+ 4, p
- 9, info
->text
) ^ info
->invert
;
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;
213 memcpy(tmp
, data
, 32);
215 printk(KERN_INFO
"data=[%s]\n", tmp
);
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
,
231 unsigned int protoff
,
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
;
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
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
);
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
[] = {
279 .matchsize
= sizeof(struct xt_web_info
),
280 .proto
= IPPROTO_TCP
,
283 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
288 .matchsize
= sizeof(struct xt_web_info
),
289 .proto
= IPPROTO_TCP
,
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
));