GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / net / netfilter / xt_webstr.c
blobdf864d5a51585149a7b0c337e578fcdd0622ad29
1 /* Kernel module to match a string into a packet.
3 * Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be>
4 *
5 * ChangeLog
6 * 19.02.2002: Gianni Tedesco <gianni@ecsc.co.uk>
7 * Fixed SMP re-entrancy problem using per-cpu data areas
8 * for the skip/shift tables.
9 * 02.05.2001: Gianni Tedesco <gianni@ecsc.co.uk>
10 * Fixed kernel panic, due to overrunning boyer moore string
11 * tables. Also slightly tweaked heuristic for deciding what
12 * search algo to use.
13 * 27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
14 * Implemented Boyer Moore Sublinear search algorithm
15 * alongside the existing linear search based on memcmp().
16 * Also a quick check to decide which method to use on a per
17 * packet basis.
20 /* Kernel module to match a http header string into a packet.
22 * Copyright (C) 2003, CyberTAN Corporation
23 * All Rights Reserved.
25 * Description:
26 * This is kernel module for web content inspection. It was derived from
27 * 'string' match module, declared as above.
29 * The module follows the Netfilter framework, called extended packet
30 * matching modules.
33 /* Linux Kernel 2.6 Port ( 2.4 ipt-> 2.6 xt)
34 * Copyright (C) 2008, Ralink Technology Corporation.
35 * All Rights Reserved.
38 #include <linux/module.h>
39 #include <linux/skbuff.h>
40 #include <linux/netfilter/x_tables.h>
41 #include <linux/ip.h>
42 #include <linux/tcp.h>
43 #include <net/sock.h>
45 #define BM_MAX_NLEN 256
46 #define BM_MAX_HLEN 1024
48 #define BLK_JAVA 0x01
49 #define BLK_ACTIVE 0x02
50 #define BLK_COOKIE 0x04
51 #define BLK_PROXY 0x08
53 typedef char *(*proc_ipt_search) (char *, char *, int, int);
55 struct ipt_webstr_info {
56 char string[BM_MAX_NLEN];
57 u_int16_t invert;
58 u_int16_t len;
59 u_int8_t type;
62 enum xt_webstr_type
64 IPT_WEBSTR_HOST,
65 IPT_WEBSTR_URL,
66 IPT_WEBSTR_CONTENT
70 #define isdigit(x) ((x) >= '0' && (x) <= '9')
71 #define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
72 #define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
73 #define isalpha(x) (isupper(x) || islower(x))
74 #define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A')
75 #define tolower(x) (isupper(x) ? ((x) - 'A' + 'a') : (x))
77 #define split(word, wordlist, next, delim) \
78 for (next = wordlist, \
79 strncpy(word, next, sizeof(word)), \
80 word[(next=strstr(next, delim)) ? strstr(word, delim) - word : sizeof(word) - 1] = '\0', \
81 next = next ? next + sizeof(delim) - 1 : NULL ; \
82 strlen(word); \
83 next = next ? : "", \
84 strncpy(word, next, sizeof(word)), \
85 word[(next=strstr(next, delim)) ? strstr(word, delim) - word : sizeof(word) - 1] = '\0', \
86 next = next ? next + sizeof(delim) - 1 : NULL)
88 #define BUFSIZE 1024
90 /* Flags for get_http_info() */
91 #define HTTP_HOST 0x01
92 #define HTTP_URL 0x02
93 /* Flags for mangle_http_header() */
94 #define HTTP_COOKIE 0x04
96 #if 0
97 #define SPARQ_LOG printk
98 #else
99 #define SPARQ_LOG(format, args...)
100 #endif
102 typedef struct httpinfo {
103 char host[BUFSIZE + 1];
104 int hostlen;
105 char url[BUFSIZE + 1];
106 int urllen;
107 } httpinfo_t;
109 /* Return 1 for match, 0 for accept, -1 for partial. */
110 static int find_pattern2(const char *data, size_t dlen,
111 const char *pattern, size_t plen,
112 char term,
113 unsigned int *numoff,
114 unsigned int *numlen)
116 size_t i, j, k;
117 int state = 0;
118 *numoff = *numlen = 0;
120 SPARQ_LOG("%s: pattern = '%s', dlen = %u\n",__FUNCTION__, pattern, dlen);
121 if (dlen == 0)
122 return 0;
124 if (dlen <= plen) { /* Short packet: try for partial? */
125 if (strnicmp(data, pattern, dlen) == 0)
126 return -1;
127 else
128 return 0;
130 for (i = 0; i <= (dlen - plen); i++) {
131 /* DFA : \r\n\r\n :: 1234 */
132 if (*(data + i) == '\r') {
133 if (!(state % 2)) state++; /* forwarding move */
134 else state = 0; /* reset */
136 else if (*(data + i) == '\n') {
137 if (state % 2) state++;
138 else state = 0;
140 else state = 0;
142 if (state >= 4)
143 break;
145 /* pattern compare */
146 if (memcmp(data + i, pattern, plen ) != 0)
147 continue;
149 /* Here, it means patten match!! */
150 *numoff=i + plen;
151 for (j = *numoff, k = 0; data[j] != term; j++, k++)
152 if (j > dlen) return -1 ; /* no terminal char */
154 *numlen = k;
155 return 1;
157 return 0;
160 #if 0
161 static int mangle_http_header(const struct sk_buff *skb, int flags)
163 struct iphdr *iph = (skb)->nh.iph;
164 struct tcphdr *tcph = (void *)iph + iph->ihl*4;
165 unsigned char *data = (void *)tcph + tcph->doff*4;
166 unsigned int datalen = (skb)->len - (iph->ihl*4) - (tcph->doff*4);
168 int found, offset, len;
169 int ret = 0;
172 SPARQ_LOG("%s: seq=%u\n", __FUNCTION__, ntohl(tcph->seq));
174 /* Basic checking, is it HTTP packet? */
175 if (datalen < 10)
176 return ret; /* Not enough length, ignore it */
177 if (memcmp(data, "GET ", sizeof("GET ") - 1) != 0 &&
178 memcmp(data, "POST ", sizeof("POST ") - 1) != 0 &&
179 memcmp(data, "HEAD ", sizeof("HEAD ") - 1) != 0) //zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15)
180 return ret; /* Pass it */
182 /* COOKIE modification */
183 if (flags & HTTP_COOKIE) {
184 found = find_pattern2(data, datalen, "Cookie: ",
185 sizeof("Cookie: ")-1, '\r', &offset, &len);
186 if (found) {
187 char c;
188 offset -= (sizeof("Cookie: ") - 1);
189 /* Swap the 2rd and 4th bit */
190 c = *(data + offset + 2) ;
191 *(data + offset + 2) = *(data + offset + 4) ;
192 *(data + offset + 4) = c ;
193 ret++;
197 return ret;
199 #endif
201 static int get_http_info(const struct sk_buff *skb, int flags, httpinfo_t *info)
203 struct iphdr *iph = ip_hdr(skb);
204 struct tcphdr *tcph = (void *)iph + iph->ihl*4;
205 unsigned char *data = (void *)tcph + tcph->doff*4;
206 unsigned int datalen = (skb)->len - (iph->ihl*4) - (tcph->doff*4);
208 int found, offset;
209 int hostlen, pathlen;
210 int ret = 0;
213 SPARQ_LOG("%s: seq=%u\n", __FUNCTION__, ntohl(tcph->seq));
215 /* Basic checking, is it HTTP packet? */
216 if (datalen < 10)
217 return ret; /* Not enough length, ignore it */
218 if (memcmp(data, "GET ", sizeof("GET ") - 1) != 0 &&
219 memcmp(data, "POST ", sizeof("POST ") - 1) != 0 &&
220 memcmp(data, "HEAD ", sizeof("HEAD ") - 1) != 0) //zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15)
221 return ret; /* Pass it */
223 if (!(flags & (HTTP_HOST | HTTP_URL)))
224 return ret;
226 /* find the 'Host: ' value */
227 found = find_pattern2(data, datalen, "Host: ",
228 sizeof("Host: ") - 1, '\r', &offset, &hostlen);
229 SPARQ_LOG("Host found=%d\n", found);
231 if (!found || !hostlen)
232 return ret;
234 ret++; /* Host found, increase the return value */
235 hostlen = (hostlen < BUFSIZE) ? hostlen : BUFSIZE;
236 strncpy(info->host, data + offset, hostlen);
237 *(info->host + hostlen) = 0; /* null-terminated */
238 info->hostlen = hostlen;
239 SPARQ_LOG("HOST=%s, hostlen=%d\n", info->host, info->hostlen);
241 if (!(flags & HTTP_URL))
242 return ret;
244 /* find the 'GET ' or 'POST ' or 'HEAD ' value */
245 found = find_pattern2(data, datalen, "GET ",
246 sizeof("GET ") - 1, '\r', &offset, &pathlen);
247 if (!found)
248 found = find_pattern2(data, datalen, "POST ",
249 sizeof("POST ") - 1, '\r', &offset, &pathlen);
250 /******* zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15) ******/
251 if (!found)
252 found = find_pattern2(data, datalen, "HEAD ",
253 sizeof("HEAD ") - 1, '\r', &offset, &pathlen);
254 /************************* zg end 2006.09.28 ****************************/
255 SPARQ_LOG("GET/POST found=%d\n", found);
257 if (!found || (pathlen -= (sizeof(" HTTP/x.x") - 1)) <= 0)/* ignor this field */
258 return ret;
260 ret++; /* GET/POST/HEAD found, increase the return value */
261 pathlen = ((pathlen + hostlen) < BUFSIZE) ? pathlen : BUFSIZE - hostlen;
262 strncpy(info->url, info->host, hostlen);
263 strncpy(info->url + hostlen, data + offset, pathlen);
264 *(info->url + hostlen + pathlen) = 0; /* null-terminated */
265 info->urllen = hostlen + pathlen;
266 SPARQ_LOG("URL=%s, urllen=%d\n", info->url, info->urllen);
268 return ret;
271 /* Linear string search based on memcmp() */
272 static char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len)
274 char *k = haystack + (haystack_len-needle_len);
275 char *t = haystack;
277 SPARQ_LOG("%s: haystack=%s, needle=%s\n", __FUNCTION__, t, needle);
278 for(; t <= k; t++) {
279 //SPARQ_LOG("%s: haystack=%s, needle=%s\n", __FUNCTION__, t, needle);
280 if (strnicmp(t, needle, needle_len) == 0) return t;
281 //if ( memcmp(t, needle, needle_len) == 0 ) return t;
284 return NULL;
288 static bool match(const struct sk_buff *skb, struct xt_action_param *par)
290 const struct ipt_webstr_info *info = par->matchinfo;
291 struct iphdr *ip = ip_hdr(skb);
292 proc_ipt_search search=search_linear;
294 char token[] = "<&nbsp;>";
295 char *wordlist = (char *)&info->string;
296 httpinfo_t htinfo;
297 int flags = 0;
298 int found = 0;
299 long int opt = 0;
302 if (!ip || info->len < 1)
303 return 0;
305 SPARQ_LOG("\n************************************************\n"
306 "%s: type=%s\n", __FUNCTION__, (info->type == IPT_WEBSTR_URL)
307 ? "IPT_WEBSTR_URL" : (info->type == IPT_WEBSTR_HOST)
308 ? "IPT_WEBSTR_HOST" : "IPT_WEBSTR_CONTENT" );
310 /* Determine the flags value for get_http_info(), and mangle packet
311 * if needed. */
312 switch(info->type)
314 case IPT_WEBSTR_URL: /* fall through */
315 flags |= HTTP_URL;
317 case IPT_WEBSTR_HOST:
318 flags |= HTTP_HOST;
319 break;
321 case IPT_WEBSTR_CONTENT:
322 opt = simple_strtol(wordlist, (char **)NULL, 10);
323 SPARQ_LOG("%s: string=%s, opt=%#lx\n", __FUNCTION__, wordlist, opt);
325 if (opt & (BLK_JAVA | BLK_ACTIVE | BLK_PROXY))
326 flags |= HTTP_URL;
327 if (opt & BLK_PROXY)
328 flags |= HTTP_HOST;
329 #if 0
330 // Could we modify the packet payload in a "match" module? --YY@Ralink
331 if (opt & BLK_COOKIE)
332 mangle_http_header(skb, HTTP_COOKIE);
333 #endif
334 break;
336 default:
337 printk("%s: Sorry! Cannot find this match option.\n", __FILE__);
338 return 0;
341 /* Get the http header info */
342 if (get_http_info(skb, flags, &htinfo) < 1)
343 return 0;
345 /* Check if the http header content contains the forbidden keyword */
346 if (info->type == IPT_WEBSTR_HOST || info->type == IPT_WEBSTR_URL) {
347 int nlen = 0, hlen = 0;
348 char needle[BUFSIZE], *haystack = NULL;
349 char *next;
351 if (info->type == IPT_WEBSTR_HOST) {
352 haystack = htinfo.host;
353 hlen = htinfo.hostlen;
355 else {
356 haystack = htinfo.url;
357 hlen = htinfo.urllen;
359 split(needle, wordlist, next, token) {
360 nlen = strlen(needle);
361 SPARQ_LOG("keyword=%s, nlen=%d, hlen=%d\n", needle, nlen, hlen);
362 if (!nlen || !hlen || nlen > hlen) continue;
363 if (search(needle, haystack, nlen, hlen) != NULL) {
364 found = 1;
365 break;
369 else { /* IPT_WEBSTR_CONTENT */
370 int vicelen;
372 if (opt & BLK_JAVA) {
373 vicelen = sizeof(".js") - 1;
374 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".js", vicelen) == 0) {
375 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__);
376 found = 1;
377 goto match_ret;
379 vicelen = sizeof(".class") - 1;
380 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".class", vicelen) == 0) {
381 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__);
382 found = 1;
383 goto match_ret;
386 if (opt & BLK_ACTIVE){
387 vicelen = sizeof(".ocx") - 1;
388 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".ocx", vicelen) == 0) {
389 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__);
390 found = 1;
391 goto match_ret;
393 vicelen = sizeof(".cab") - 1;
394 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".cab", vicelen) == 0) {
395 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__);
396 found = 1;
397 goto match_ret;
400 if (opt & BLK_PROXY){
401 if (strnicmp(htinfo.url + htinfo.hostlen, "http://", sizeof("http://") - 1) == 0) {
402 SPARQ_LOG("%s: MATCH....proxy\n", __FUNCTION__);
403 found = 1;
404 goto match_ret;
409 match_ret:
410 SPARQ_LOG("%s: Verdict =======> %s \n",__FUNCTION__
411 , found ? "DROP" : "ACCEPT");
413 return (found ^ info->invert);
416 static int checkentry(const struct xt_mtchk_param *par)
418 #if 0
419 if (matchsize != IPT_ALIGN(sizeof(struct ipt_webstr_info)))
420 return 0;
421 #endif
422 return 0;
425 static struct xt_match xt_webstr_match[] = {
427 .name = "webstr",
428 .family = AF_INET,
429 .match = match,
430 .checkentry = checkentry,
431 .matchsize = sizeof(struct ipt_webstr_info),
432 .me = THIS_MODULE
435 .name = "webstr",
436 .family = AF_INET6,
437 .match = match,
438 .checkentry = checkentry,
439 .matchsize = sizeof(struct ipt_webstr_info),
440 .me = THIS_MODULE
445 static int __init init(void)
447 return xt_register_matches(xt_webstr_match, ARRAY_SIZE(xt_webstr_match));
450 static void __exit fini(void)
452 xt_unregister_matches(xt_webstr_match, ARRAY_SIZE(xt_webstr_match));
455 module_init(init);
456 module_exit(fini);