1/* 2 3 web (experimental) 4 HTTP client request match 5 Copyright (C) 2006 Jonathan Zarate 6 7 Licensed under GNU GPL v2 or later. 8 9*/ 10#include <linux/module.h> 11#include <linux/skbuff.h> 12#include <linux/version.h> 13#include <linux/ip.h> 14#include <linux/tcp.h> 15#include <net/sock.h> 16#include <linux/netfilter_ipv4/ip_tables.h> 17#include <linux/netfilter_ipv4/ipt_web.h> 18 19MODULE_AUTHOR("Jonathan Zarate"); 20MODULE_DESCRIPTION("HTTP client request match (experimental)"); 21MODULE_LICENSE("GPL"); 22 23#define LOG(...) do { } while (0); 24 25static int find(const char *data, const char *tail, const char *text) 26{ 27 int n, o; 28 int dlen; 29 const char *p, *e; 30 31 while ((data < tail) && (*data == ' ')) ++data; 32 while ((tail > data) && (*(tail - 1) == ' ')) --tail; 33 34 dlen = tail - data; 35 36 // 012345 37 // text 38 // ^text 39 // text$ 40 // ^text$ 41 // 012345 42 43 while (*text) { 44 n = o = strlen(text); 45 if (*text == '^') { 46 --n; 47 if (*(text + n) == '$') { 48 // exact 49 --n; 50 if ((dlen == n) && (memcmp(data, text + 1, n) == 0)) { 51 LOG(KERN_INFO "matched %s\n", text); 52 return 1; 53 } 54 } 55 else { 56 // begins with 57 if ((dlen >= n) && (memcmp(data, text + 1, n) == 0)) { 58 LOG(KERN_INFO "matched %s\n", text); 59 return 1; 60 } 61 } 62 } 63 else if (*(text + n - 1) == '$') { 64 // ends with 65 --n; 66 if (memcmp(tail - n, text, n) == 0) { 67 LOG(KERN_INFO "matched %s\n", text); 68 return 1; 69 } 70 } 71 else { 72 // contains 73 p = data; 74 e = tail - n; 75 while (p <= e) { 76 if (memcmp(p, text, n) == 0) { 77 LOG(KERN_INFO "matched %s\n", text); 78 return 1; 79 } 80 ++p; 81 } 82 } 83 84 text += o + 1; 85 } 86 return 0; 87} 88 89static inline const char *findend(const char *data, const char *tail, int min) 90{ 91 int n = tail - data; 92 if (n >= min) { 93 while (data < tail) { 94 if (*data == '\r') return data; 95 ++data; 96 } 97 } 98 return NULL; 99} 100 101 102static bool 103match(const struct sk_buff *skb, struct xt_action_param *par) 104{ 105 const struct ipt_web_info *info = par->matchinfo; 106 const int offset = par->fragoff; 107 const struct iphdr *iph = ip_hdr(skb); 108 const struct tcphdr *tcph = (void *)iph + iph->ihl * 4; 109 const char *data; 110 const char *tail; 111 const char *p, *q; 112 int doff, dlen; 113 __u32 sig; 114 115 if (offset != 0) return info->invert; 116 117 doff = (tcph->doff * 4); 118 data = (void *)tcph + doff; 119 dlen = ntohs(ip_hdr(skb)->tot_len); 120 121 // POST / HTTP/1.0$$$$ 122 // GET / HTTP/1.0$$$$ 123 // 1234567890123456789 124 if (dlen < 18) return info->invert; 125 126 // "GET " or "POST" 127 sig = *(__u32 *)data; 128 if ((sig != __constant_htonl(0x47455420)) && (sig != __constant_htonl(0x504f5354))) { 129 return info->invert; 130 } 131 132 tail = data + dlen; 133 if (dlen > 1024) { 134 dlen = 1024; 135 tail = data + 1024; 136 } 137 138 // POST / HTTP/1.0$$$$ 139 // GET / HTTP/1.0$$$$ -- minimum 140 // 0123456789012345678 141 // 9876543210 142 if (((p = findend(data + 14, tail, 18)) == NULL) || (memcmp(p - 9, " HTTP/", 6) != 0)) 143 return info->invert; 144 145 switch (info->mode) { 146 case IPT_WEB_HTTP: 147 return !info->invert; 148 case IPT_WEB_HORE: 149 // entire request line, else host line 150 if (find(data + 4, p - 9, info->text)) return !info->invert; 151 break; 152 case IPT_WEB_PATH: 153 // left side of '?' or entire line 154 q = data += 4; 155 p -= 9; 156 while ((q < p) && (*q != '?')) ++q; 157 return find(data, q, info->text) ^ info->invert; 158 case IPT_WEB_QUERY: 159 // right side of '?' or none 160 q = data + 4; 161 p -= 9; 162 while ((q < p) && (*q != '?')) ++q; 163 if (q >= p) return info->invert; 164 return find(q + 1, p, info->text) ^ info->invert; 165 case IPT_WEB_RURI: 166 // entire request line 167 return find(data + 4, p - 9, info->text) ^ info->invert; 168 default: 169 // shutup compiler 170 break; 171 } 172 173 // else, IPT_WEB_HOST 174 175 while (1) { 176 data = p + 2; // skip previous \r\n 177 p = findend(data, tail, 8); // p = current line's \r 178 if (p == NULL) return 0; 179 180 if (memcmp(data, "Host: ", 6) == 0) 181 return find(data + 6, p, info->text) ^ info->invert; 182 } 183 184 return !info->invert; 185} 186 187static int 188checkentry(const struct xt_mtchk_param *par) 189{ 190 return 0; 191} 192 193static struct xt_match web_match = { 194 .name = "web", 195 .family = AF_INET, 196 .match = &match, 197 .matchsize = sizeof(struct ipt_web_info), 198 .checkentry = &checkentry, 199 .destroy = NULL, 200 .me = THIS_MODULE 201}; 202 203static int __init init(void) 204{ 205 return xt_register_match(&web_match); 206} 207 208static void __exit fini(void) 209{ 210 xt_unregister_match(&web_match); 211} 212 213module_init(init); 214module_exit(fini); 215