1 /* 2 * alias_smedia.c 3 * 4 * Copyright (c) 2000 Whistle Communications, Inc. 5 * All rights reserved. 6 * 7 * Subject to the following obligations and disclaimer of warranty, use and 8 * redistribution of this software, in source or object code forms, with or 9 * without modifications are expressly permitted by Whistle Communications; 10 * provided, however, that: 11 * 1. Any and all reproductions of the source or object code must include the 12 * copyright notice above and the following disclaimer of warranties; and 13 * 2. No rights are granted, in any manner or form, to use Whistle 14 * Communications, Inc. trademarks, including the mark "WHISTLE 15 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 16 * such appears in the above copyright notice or in the software. 17 * 18 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 19 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 20 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 21 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 23 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 24 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 25 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 26 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 27 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 28 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 29 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 30 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 34 * OF SUCH DAMAGE. 35 * 36 * Copyright (c) 2000 Junichi SATOH <junichi@astec.co.jp> 37 * <junichi@junichi.org> 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 * 61 * Authors: Erik Salander <erik@whistle.com> 62 * Junichi SATOH <junichi@astec.co.jp> 63 * <junichi@junichi.org> 64 * 65 * $FreeBSD$ 66 */ 67 68 /* 69 Alias_smedia.c is meant to contain the aliasing code for streaming media 70 protocols. It performs special processing for RSTP sessions under TCP. 71 Specifically, when a SETUP request is sent by a client, or a 200 reply 72 is sent by a server, it is intercepted and modified. The address is 73 changed to the gateway machine and an aliasing port is used. 74 75 More specifically, the "client_port" configuration parameter is 76 parsed for SETUP requests. The "server_port" configuration parameter is 77 parsed for 200 replies eminating from a server. This is intended to handle 78 the unicast case. 79 80 RTSP also allows a redirection of a stream to another client by using the 81 "destination" configuration parameter. The destination config parm would 82 indicate a different IP address. This function is NOT supported by the 83 RTSP translation code below. 84 85 The RTSP multicast functions without any address translation intervention. 86 87 For this routine to work, the SETUP/200 must fit entirely 88 into a single TCP packet. This is typically the case, but exceptions 89 can easily be envisioned under the actual specifications. 90 91 Probably the most troubling aspect of the approach taken here is 92 that the new SETUP/200 will typically be a different length, and 93 this causes a certain amount of bookkeeping to keep track of the 94 changes of sequence and acknowledgment numbers, since the client 95 machine is totally unaware of the modification to the TCP stream. 96 97 Initial version: May, 2000 (eds) 98 */ 99 100 #include <stdio.h> 101 #include <string.h> 102 #include <sys/types.h> 103 #include <netinet/in_systm.h> 104 #include <netinet/in.h> 105 #include <netinet/ip.h> 106 #include <netinet/tcp.h> 107 #include <netinet/udp.h> 108 109 #include "alias_local.h" 110 111 #define RTSP_CONTROL_PORT_NUMBER_1 554 112 #define RTSP_CONTROL_PORT_NUMBER_2 7070 113 #define RTSP_PORT_GROUP 2 114 115 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) 116 117 static int 118 search_string(char *data, int dlen, const char *search_str) 119 { 120 int i, j, k; 121 int search_str_len; 122 123 search_str_len = strlen(search_str); 124 for (i = 0; i < dlen - search_str_len; i++) { 125 for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { 126 if (data[j] != search_str[k] && 127 data[j] != search_str[k] - ('a' - 'A')) { 128 break; 129 } 130 if (k == search_str_len - 1) { 131 return j + 1; 132 } 133 } 134 } 135 return -1; 136 } 137 138 static int 139 alias_rtsp_out(struct ip *pip, 140 struct alias_link *link, 141 char *data, 142 const char *port_str) 143 { 144 int hlen, tlen, dlen; 145 struct tcphdr *tc; 146 int i, j, pos, state, port_dlen, new_dlen, delta; 147 u_short p[2], new_len; 148 u_short sport, eport, base_port; 149 u_short salias = 0, ealias = 0, base_alias = 0; 150 const char *transport_str = "transport:"; 151 char newdata[2048], *port_data, *port_newdata, stemp[80]; 152 int links_created = 0, pkt_updated = 0; 153 struct alias_link *rtsp_link = NULL; 154 struct in_addr null_addr; 155 156 /* Calculate data length of TCP packet */ 157 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 158 hlen = (pip->ip_hl + tc->th_off) << 2; 159 tlen = ntohs(pip->ip_len); 160 dlen = tlen - hlen; 161 162 /* Find keyword, "Transport: " */ 163 pos = search_string(data, dlen, transport_str); 164 if (pos < 0) { 165 return -1; 166 } 167 port_data = data + pos; 168 port_dlen = dlen - pos; 169 170 memcpy(newdata, data, pos); 171 port_newdata = newdata + pos; 172 173 while (port_dlen > strlen(port_str)) { 174 /* Find keyword, appropriate port string */ 175 pos = search_string(port_data, port_dlen, port_str); 176 if (pos < 0) { 177 break; 178 } 179 180 memcpy (port_newdata, port_data, pos + 1); 181 port_newdata += (pos + 1); 182 183 p[0] = p[1] = 0; 184 sport = eport = 0; 185 state = 0; 186 for (i = pos; i < port_dlen; i++) { 187 switch(state) { 188 case 0: 189 if (port_data[i] == '=') { 190 state++; 191 } 192 break; 193 case 1: 194 if (ISDIGIT(port_data[i])) { 195 p[0] = p[0] * 10 + port_data[i] - '0'; 196 } else { 197 if (port_data[i] == ';') { 198 state = 3; 199 } 200 if (port_data[i] == '-') { 201 state++; 202 } 203 } 204 break; 205 case 2: 206 if (ISDIGIT(port_data[i])) { 207 p[1] = p[1] * 10 + port_data[i] - '0'; 208 } else { 209 state++; 210 } 211 break; 212 case 3: 213 base_port = p[0]; 214 sport = htons(p[0]); 215 eport = htons(p[1]); 216 217 if (!links_created) { 218 219 links_created = 1; 220 /* Find an even numbered port number base that 221 satisfies the contiguous number of ports we need */ 222 null_addr.s_addr = 0; 223 if (0 == (salias = FindNewPortGroup(null_addr, 224 FindAliasAddress(pip->ip_src), 225 sport, 0, 226 RTSP_PORT_GROUP, 227 IPPROTO_UDP, 1))) { 228 #ifdef DEBUG 229 fprintf(stderr, 230 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 231 #endif 232 } else { 233 234 base_alias = ntohs(salias); 235 for (j = 0; j < RTSP_PORT_GROUP; j++) { 236 /* Establish link to port found in RTSP packet */ 237 rtsp_link = FindRtspOut(GetOriginalAddress(link), null_addr, 238 htons(base_port + j), htons(base_alias + j), 239 IPPROTO_UDP); 240 if (rtsp_link != NULL) { 241 #ifndef NO_FW_PUNCH 242 /* Punch hole in firewall */ 243 PunchFWHole(rtsp_link); 244 #endif 245 } else { 246 #ifdef DEBUG 247 fprintf(stderr, 248 "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); 249 #endif 250 break; 251 } 252 } 253 } 254 ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); 255 } 256 257 if (salias && rtsp_link) { 258 259 pkt_updated = 1; 260 261 /* Copy into IP packet */ 262 sprintf(stemp, "%d", ntohs(salias)); 263 memcpy(port_newdata, stemp, strlen(stemp)); 264 port_newdata += strlen(stemp); 265 266 if (eport != 0) { 267 *port_newdata = '-'; 268 port_newdata++; 269 270 /* Copy into IP packet */ 271 sprintf(stemp, "%d", ntohs(ealias)); 272 memcpy(port_newdata, stemp, strlen(stemp)); 273 port_newdata += strlen(stemp); 274 } 275 276 *port_newdata = ';'; 277 port_newdata++; 278 } 279 state++; 280 break; 281 } 282 if (state > 3) { 283 break; 284 } 285 } 286 port_data += i; 287 port_dlen -= i; 288 } 289 290 if (!pkt_updated) 291 return -1; 292 293 memcpy (port_newdata, port_data, port_dlen); 294 port_newdata += port_dlen; 295 *port_newdata = '\0'; 296 297 /* Create new packet */ 298 new_dlen = port_newdata - newdata; 299 memcpy (data, newdata, new_dlen); 300 301 SetAckModified(link); 302 delta = GetDeltaSeqOut(pip, link); 303 AddSeq(pip, link, delta + new_dlen - dlen); 304 305 new_len = htons(hlen + new_dlen); 306 DifferentialChecksum(&pip->ip_sum, 307 &new_len, 308 &pip->ip_len, 309 1); 310 pip->ip_len = new_len; 311 312 tc->th_sum = 0; 313 tc->th_sum = TcpChecksum(pip); 314 315 return 0; 316 } 317 318 /* Support the protocol used by early versions of RealPlayer */ 319 320 static int 321 alias_pna_out(struct ip *pip, 322 struct alias_link *link, 323 char *data, 324 int dlen) 325 { 326 struct alias_link *pna_links; 327 u_short msg_id, msg_len; 328 char *work; 329 u_short alias_port, port; 330 struct tcphdr *tc; 331 332 work = data; 333 work += 5; 334 while (work + 4 < data + dlen) { 335 memcpy(&msg_id, work, 2); 336 work += 2; 337 memcpy(&msg_len, work, 2); 338 work += 2; 339 if (ntohs(msg_id) == 0) { 340 /* end of options */ 341 return 0; 342 } 343 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 344 memcpy(&port, work, 2); 345 pna_links = FindUdpTcpOut(pip->ip_src, GetDestAddress(link), 346 port, 0, IPPROTO_UDP, 1); 347 if (pna_links != NULL) { 348 #ifndef NO_FW_PUNCH 349 /* Punch hole in firewall */ 350 PunchFWHole(pna_links); 351 #endif 352 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 353 alias_port = GetAliasPort(pna_links); 354 memcpy(work, &alias_port, 2); 355 356 /* Compute TCP checksum for revised packet */ 357 tc->th_sum = 0; 358 tc->th_sum = TcpChecksum(pip); 359 } 360 } 361 work += ntohs(msg_len); 362 } 363 364 return 0; 365 } 366 367 void 368 AliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize) 369 { 370 int hlen, tlen, dlen; 371 struct tcphdr *tc; 372 char *data; 373 const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 374 const char *okstr = "OK", *client_port_str = "client_port"; 375 const char *server_port_str = "server_port"; 376 int i, parseOk; 377 378 tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); 379 hlen = (pip->ip_hl + tc->th_off) << 2; 380 tlen = ntohs(pip->ip_len); 381 dlen = tlen - hlen; 382 383 data = (char*)pip; 384 data += hlen; 385 386 /* When aliasing a client, check for the SETUP request */ 387 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 388 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 389 390 if (dlen >= strlen(setup)) { 391 if (memcmp(data, setup, strlen(setup)) == 0) { 392 alias_rtsp_out(pip, link, data, client_port_str); 393 return; 394 } 395 } 396 if (dlen >= strlen(pna)) { 397 if (memcmp(data, pna, strlen(pna)) == 0) { 398 alias_pna_out(pip, link, data, dlen); 399 } 400 } 401 402 } else { 403 404 /* When aliasing a server, check for the 200 reply 405 Accomodate varying number of blanks between 200 & OK */ 406 407 if (dlen >= strlen(str200)) { 408 409 for (parseOk = 0, i = 0; 410 i <= dlen - strlen(str200); 411 i++) { 412 if (memcmp(&data[i], str200, strlen(str200)) == 0) { 413 parseOk = 1; 414 break; 415 } 416 } 417 if (parseOk) { 418 419 i += strlen(str200); /* skip string found */ 420 while(data[i] == ' ') /* skip blank(s) */ 421 i++; 422 423 if ((dlen - i) >= strlen(okstr)) { 424 425 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 426 alias_rtsp_out(pip, link, data, server_port_str); 427 428 } 429 } 430 } 431 } 432 } 433