1/* MMS extension for IP connection tracking 2 * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be> 3 * based on ip_conntrack_ftp.c and ip_conntrack_irc.c 4 * 5 * ip_conntrack_mms.c v0.3 2002-09-22 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * Module load syntax: 13 * insmod ip_conntrack_mms.o ports=port1,port2,...port<MAX_PORTS> 14 * 15 * Please give the ports of all MMS servers You wish to connect to. 16 * If you don't specify ports, the default will be TCP port 1755. 17 * 18 * More info on MMS protocol, firewalls and NAT: 19 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp 20 * http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp 21 * 22 * The SDP project people are reverse-engineering MMS: 23 * http://get.to/sdp 24 */ 25 26#include <linux/config.h> 27#include <linux/module.h> 28#include <linux/netfilter.h> 29#include <linux/ip.h> 30#include <linux/ctype.h> 31#include <net/checksum.h> 32#include <net/tcp.h> 33 34#include <linux/netfilter_ipv4/lockhelp.h> 35#include <linux/netfilter_ipv4/ip_conntrack_helper.h> 36#include <linux/netfilter_ipv4/ip_conntrack_mms.h> 37 38DECLARE_LOCK(ip_mms_lock); 39struct module *ip_conntrack_mms = THIS_MODULE; 40 41#define MAX_PORTS 8 42static int ports[MAX_PORTS]; 43static int ports_c; 44#ifdef MODULE_PARM 45MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); 46#endif 47 48#define DEBUGP(format, args...) 49 50#ifdef CONFIG_IP_NF_NAT_NEEDED 51EXPORT_SYMBOL(ip_mms_lock); 52#endif 53 54MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>"); 55MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) connection tracking module"); 56MODULE_LICENSE("GPL"); 57 58/* #define isdigit(c) (c >= '0' && c <= '9') */ 59 60/* copied from drivers/usb/serial/io_edgeport.c - not perfect but will do the trick */ 61static void unicode_to_ascii (char *string, short *unicode, int unicode_size) 62{ 63 int i; 64 for (i = 0; i < unicode_size; ++i) { 65 string[i] = (char)(unicode[i]); 66 } 67 string[unicode_size] = 0x00; 68} 69 70__inline static int atoi(char *s) 71{ 72 int i=0; 73 while (isdigit(*s)) { 74 i = i*10 + *(s++) - '0'; 75 } 76 return i; 77} 78 79/* convert ip address string like "192.168.0.10" to unsigned int */ 80__inline static u_int32_t asciiiptoi(char *s) 81{ 82 unsigned int i, j, k; 83 84 for(i=k=0; k<3; ++k, ++s, i<<=8) { 85 i+=atoi(s); 86 for(j=0; (*(++s) != '.') && (j<3); ++j) 87 ; 88 } 89 i+=atoi(s); 90 return ntohl(i); 91} 92 93int parse_mms(const char *data, 94 const unsigned int datalen, 95 u_int32_t *mms_ip, 96 u_int16_t *mms_proto, 97 u_int16_t *mms_port, 98 char **mms_string_b, 99 char **mms_string_e, 100 char **mms_padding_e) 101{ 102 int unicode_size, i; 103 char tempstring[28]; /* "\\255.255.255.255\UDP\65535" */ 104 char getlengthstring[28]; 105 106 for(unicode_size=0; 107 (char) *(data+(MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2)) != (char)0; 108 unicode_size++) 109 if ((unicode_size == 28) || (MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2 >= datalen)) 110 return -1; /* out of bounds - incomplete packet */ 111 112 unicode_to_ascii(tempstring, (short *)(data+MMS_SRV_UNICODE_STRING_OFFSET), unicode_size); 113 DEBUGP("ip_conntrack_mms: offset 60: %s\n", (const char *)(tempstring)); 114 115 /* IP address ? */ 116 *mms_ip = asciiiptoi(tempstring+2); 117 118 i=sprintf(getlengthstring, "%u.%u.%u.%u", HIPQUAD(*mms_ip)); 119 120 /* protocol ? */ 121 if(strncmp(tempstring+3+i, "TCP", 3)==0) 122 *mms_proto = IPPROTO_TCP; 123 else if(strncmp(tempstring+3+i, "UDP", 3)==0) 124 *mms_proto = IPPROTO_UDP; 125 126 /* port ? */ 127 *mms_port = atoi(tempstring+7+i); 128 129 /* we store a pointer to the beginning of the "\\a.b.c.d\proto\port" 130 unicode string, one to the end of the string, and one to the end 131 of the packet, since we must keep track of the number of bytes 132 between end of the unicode string and the end of packet (padding) */ 133 *mms_string_b = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET); 134 *mms_string_e = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET + unicode_size * 2); 135 *mms_padding_e = (char *)(data + datalen); /* looks funny, doesn't it */ 136 return 0; 137} 138 139 140static int help(const struct iphdr *iph, size_t len, 141 struct ip_conntrack *ct, 142 enum ip_conntrack_info ctinfo) 143{ 144 /* tcplen not negative guaranteed by ip_conntrack_tcp.c */ 145 struct tcphdr *tcph = (void *)iph + iph->ihl * 4; 146 const char *data = (const char *)tcph + tcph->doff * 4; 147 unsigned int tcplen = len - iph->ihl * 4; 148 unsigned int datalen = tcplen - tcph->doff * 4; 149 int dir = CTINFO2DIR(ctinfo); 150 struct ip_conntrack_expect expect, *exp = &expect; 151 struct ip_ct_mms_expect *exp_mms_info = &exp->help.exp_mms_info; 152 153 u_int32_t mms_ip; 154 u_int16_t mms_proto; 155 char mms_proto_string[8]; 156 u_int16_t mms_port; 157 char *mms_string_b, *mms_string_e, *mms_padding_e; 158 159 /* Until there's been traffic both ways, don't look in packets. */ 160 if (ctinfo != IP_CT_ESTABLISHED 161 && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { 162 DEBUGP("ip_conntrack_mms: Conntrackinfo = %u\n", ctinfo); 163 return NF_ACCEPT; 164 } 165 166 /* Not whole TCP header? */ 167 if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) { 168 DEBUGP("ip_conntrack_mms: tcplen = %u\n", (unsigned)tcplen); 169 return NF_ACCEPT; 170 } 171 172 /* Checksum invalid? Ignore. */ 173 if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, 174 csum_partial((char *)tcph, tcplen, 0))) { 175 DEBUGP("mms_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", 176 tcph, tcplen, NIPQUAD(iph->saddr), 177 NIPQUAD(iph->daddr)); 178 return NF_ACCEPT; 179 } 180 181 /* Only look at packets with 0x00030002/196610 on bytes 36->39 of TCP payload */ 182 if( (MMS_SRV_MSG_OFFSET < datalen) && 183 ((*(u32 *)(data+MMS_SRV_MSG_OFFSET)) == MMS_SRV_MSG_ID)) { 184 DEBUGP("ip_conntrack_mms: offset 37: %u %u %u %u, datalen:%u\n", 185 (u8)*(data+36), (u8)*(data+37), 186 (u8)*(data+38), (u8)*(data+39), 187 datalen); 188 if(parse_mms(data, datalen, &mms_ip, &mms_proto, &mms_port, 189 &mms_string_b, &mms_string_e, &mms_padding_e)) 190 if(net_ratelimit()) 191 printk(KERN_WARNING 192 "ip_conntrack_mms: Unable to parse data payload\n"); 193 194 memset(&expect, 0, sizeof(expect)); 195 196 sprintf(mms_proto_string, "(%u)", mms_proto); 197 DEBUGP("ip_conntrack_mms: adding %s expectation %u.%u.%u.%u -> %u.%u.%u.%u:%u\n", 198 mms_proto == IPPROTO_TCP ? "TCP" 199 : mms_proto == IPPROTO_UDP ? "UDP":mms_proto_string, 200 NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), 201 NIPQUAD(mms_ip), 202 mms_port); 203 204 /* it's possible that the client will just ask the server to tunnel 205 the stream over the same TCP session (from port 1755): there's 206 shouldn't be a need to add an expectation in that case, but it 207 makes NAT packet mangling so much easier */ 208 LOCK_BH(&ip_mms_lock); 209 210 DEBUGP("ip_conntrack_mms: tcph->seq = %u\n", tcph->seq); 211 212 exp->seq = ntohl(tcph->seq) + (mms_string_b - data); 213 exp_mms_info->len = (mms_string_e - mms_string_b); 214 exp_mms_info->padding = (mms_padding_e - mms_string_e); 215 exp_mms_info->port = mms_port; 216 217 DEBUGP("ip_conntrack_mms: wrote info seq=%u (ofs=%u), len=%d, padding=%u\n", 218 exp->seq, (mms_string_e - data), exp_mms_info->len, exp_mms_info->padding); 219 220 exp->tuple = ((struct ip_conntrack_tuple) 221 { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, 222 { mms_ip, 223 { (__u16) ntohs(mms_port) }, 224 mms_proto } } 225 ); 226 exp->mask = ((struct ip_conntrack_tuple) 227 { { 0xFFFFFFFF, { 0 } }, 228 { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); 229 exp->expectfn = NULL; 230 ip_conntrack_expect_related(ct, &expect); 231 UNLOCK_BH(&ip_mms_lock); 232 } 233 234 return NF_ACCEPT; 235} 236 237static struct ip_conntrack_helper mms[MAX_PORTS]; 238static char mms_names[MAX_PORTS][10]; 239 240/* Not __exit: called from init() */ 241static void fini(void) 242{ 243 int i; 244 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { 245 DEBUGP("ip_conntrack_mms: unregistering helper for port %d\n", 246 ports[i]); 247 ip_conntrack_helper_unregister(&mms[i]); 248 } 249} 250 251static int __init init(void) 252{ 253 int i, ret; 254 char *tmpname; 255 256 if (ports[0] == 0) 257 ports[0] = MMS_PORT; 258 259 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { 260 memset(&mms[i], 0, sizeof(struct ip_conntrack_helper)); 261 mms[i].tuple.src.u.tcp.port = htons(ports[i]); 262 mms[i].tuple.dst.protonum = IPPROTO_TCP; 263 mms[i].mask.src.u.tcp.port = 0xFFFF; 264 mms[i].mask.dst.protonum = 0xFFFF; 265 mms[i].max_expected = 1; 266 mms[i].timeout = 0; 267 mms[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; 268 mms[i].me = THIS_MODULE; 269 mms[i].help = help; 270 271 tmpname = &mms_names[i][0]; 272 if (ports[i] == MMS_PORT) 273 sprintf(tmpname, "mms"); 274 else 275 sprintf(tmpname, "mms-%d", ports[i]); 276 mms[i].name = tmpname; 277 278 DEBUGP("ip_conntrack_mms: registering helper for port %d\n", 279 ports[i]); 280 ret = ip_conntrack_helper_register(&mms[i]); 281 282 if (ret) { 283 fini(); 284 return ret; 285 } 286 ports_c++; 287 } 288 return 0; 289} 290 291module_init(init); 292module_exit(fini); 293