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 66 #include <sys/cdefs.h> 67 __FBSDID("$FreeBSD$"); 68 69 /* 70 Alias_smedia.c is meant to contain the aliasing code for streaming media 71 protocols. It performs special processing for RSTP sessions under TCP. 72 Specifically, when a SETUP request is sent by a client, or a 200 reply 73 is sent by a server, it is intercepted and modified. The address is 74 changed to the gateway machine and an aliasing port is used. 75 76 More specifically, the "client_port" configuration parameter is 77 parsed for SETUP requests. The "server_port" configuration parameter is 78 parsed for 200 replies eminating from a server. This is intended to handle 79 the unicast case. 80 81 RTSP also allows a redirection of a stream to another client by using the 82 "destination" configuration parameter. The destination config parm would 83 indicate a different IP address. This function is NOT supported by the 84 RTSP translation code below. 85 86 The RTSP multicast functions without any address translation intervention. 87 88 For this routine to work, the SETUP/200 must fit entirely 89 into a single TCP packet. This is typically the case, but exceptions 90 can easily be envisioned under the actual specifications. 91 92 Probably the most troubling aspect of the approach taken here is 93 that the new SETUP/200 will typically be a different length, and 94 this causes a certain amount of bookkeeping to keep track of the 95 changes of sequence and acknowledgment numbers, since the client 96 machine is totally unaware of the modification to the TCP stream. 97 98 Initial version: May, 2000 (eds) 99 */ 100 101 #ifdef _KERNEL 102 #include <sys/param.h> 103 #include <sys/libkern.h> 104 #else 105 #include <sys/types.h> 106 #include <stdio.h> 107 #include <string.h> 108 #endif 109 110 #include <netinet/in_systm.h> 111 #include <netinet/in.h> 112 #include <netinet/ip.h> 113 #include <netinet/tcp.h> 114 #include <netinet/udp.h> 115 116 #ifdef _KERNEL 117 #include <netinet/libalias/alias.h> 118 #include <netinet/libalias/alias_local.h> 119 #else 120 #include "alias_local.h" 121 #endif 122 123 #define RTSP_CONTROL_PORT_NUMBER_1 554 124 #define RTSP_CONTROL_PORT_NUMBER_2 7070 125 #define RTSP_PORT_GROUP 2 126 127 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) 128 129 static int 130 search_string(char *data, int dlen, const char *search_str) 131 { 132 int i, j, k; 133 int search_str_len; 134 135 search_str_len = strlen(search_str); 136 for (i = 0; i < dlen - search_str_len; i++) { 137 for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { 138 if (data[j] != search_str[k] && 139 data[j] != search_str[k] - ('a' - 'A')) { 140 break; 141 } 142 if (k == search_str_len - 1) { 143 return (j + 1); 144 } 145 } 146 } 147 return (-1); 148 } 149 150 static int 151 alias_rtsp_out(struct libalias *la, struct ip *pip, 152 struct alias_link *lnk, 153 char *data, 154 const char *port_str) 155 { 156 int hlen, tlen, dlen; 157 struct tcphdr *tc; 158 int i, j, pos, state, port_dlen, new_dlen, delta; 159 u_short p[2], new_len; 160 u_short sport, eport, base_port; 161 u_short salias = 0, ealias = 0, base_alias = 0; 162 const char *transport_str = "transport:"; 163 char newdata[2048], *port_data, *port_newdata, stemp[80]; 164 int links_created = 0, pkt_updated = 0; 165 struct alias_link *rtsp_lnk = NULL; 166 struct in_addr null_addr; 167 168 /* Calculate data length of TCP packet */ 169 tc = (struct tcphdr *)ip_next(pip); 170 hlen = (pip->ip_hl + tc->th_off) << 2; 171 tlen = ntohs(pip->ip_len); 172 dlen = tlen - hlen; 173 174 /* Find keyword, "Transport: " */ 175 pos = search_string(data, dlen, transport_str); 176 if (pos < 0) { 177 return (-1); 178 } 179 port_data = data + pos; 180 port_dlen = dlen - pos; 181 182 memcpy(newdata, data, pos); 183 port_newdata = newdata + pos; 184 185 while (port_dlen > (int)strlen(port_str)) { 186 /* Find keyword, appropriate port string */ 187 pos = search_string(port_data, port_dlen, port_str); 188 if (pos < 0) { 189 break; 190 } 191 memcpy(port_newdata, port_data, pos + 1); 192 port_newdata += (pos + 1); 193 194 p[0] = p[1] = 0; 195 sport = eport = 0; 196 state = 0; 197 for (i = pos; i < port_dlen; i++) { 198 switch (state) { 199 case 0: 200 if (port_data[i] == '=') { 201 state++; 202 } 203 break; 204 case 1: 205 if (ISDIGIT(port_data[i])) { 206 p[0] = p[0] * 10 + port_data[i] - '0'; 207 } else { 208 if (port_data[i] == ';') { 209 state = 3; 210 } 211 if (port_data[i] == '-') { 212 state++; 213 } 214 } 215 break; 216 case 2: 217 if (ISDIGIT(port_data[i])) { 218 p[1] = p[1] * 10 + port_data[i] - '0'; 219 } else { 220 state++; 221 } 222 break; 223 case 3: 224 base_port = p[0]; 225 sport = htons(p[0]); 226 eport = htons(p[1]); 227 228 if (!links_created) { 229 230 links_created = 1; 231 /* 232 * Find an even numbered port 233 * number base that satisfies the 234 * contiguous number of ports we 235 * need 236 */ 237 null_addr.s_addr = 0; 238 if (0 == (salias = FindNewPortGroup(la, null_addr, 239 FindAliasAddress(la, pip->ip_src), 240 sport, 0, 241 RTSP_PORT_GROUP, 242 IPPROTO_UDP, 1))) { 243 #ifdef LIBALIAS_DEBUG 244 fprintf(stderr, 245 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 246 #endif 247 } else { 248 249 base_alias = ntohs(salias); 250 for (j = 0; j < RTSP_PORT_GROUP; j++) { 251 /* 252 * Establish link 253 * to port found in 254 * RTSP packet 255 */ 256 rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, 257 htons(base_port + j), htons(base_alias + j), 258 IPPROTO_UDP); 259 if (rtsp_lnk != NULL) { 260 #ifndef NO_FW_PUNCH 261 /* 262 * Punch 263 * hole in 264 * firewall 265 */ 266 PunchFWHole(rtsp_lnk); 267 #endif 268 } else { 269 #ifdef LIBALIAS_DEBUG 270 fprintf(stderr, 271 "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); 272 #endif 273 break; 274 } 275 } 276 } 277 ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); 278 } 279 if (salias && rtsp_lnk) { 280 281 pkt_updated = 1; 282 283 /* Copy into IP packet */ 284 sprintf(stemp, "%d", ntohs(salias)); 285 memcpy(port_newdata, stemp, strlen(stemp)); 286 port_newdata += strlen(stemp); 287 288 if (eport != 0) { 289 *port_newdata = '-'; 290 port_newdata++; 291 292 /* Copy into IP packet */ 293 sprintf(stemp, "%d", ntohs(ealias)); 294 memcpy(port_newdata, stemp, strlen(stemp)); 295 port_newdata += strlen(stemp); 296 } 297 *port_newdata = ';'; 298 port_newdata++; 299 } 300 state++; 301 break; 302 } 303 if (state > 3) { 304 break; 305 } 306 } 307 port_data += i; 308 port_dlen -= i; 309 } 310 311 if (!pkt_updated) 312 return (-1); 313 314 memcpy(port_newdata, port_data, port_dlen); 315 port_newdata += port_dlen; 316 *port_newdata = '\0'; 317 318 /* Create new packet */ 319 new_dlen = port_newdata - newdata; 320 memcpy(data, newdata, new_dlen); 321 322 SetAckModified(lnk); 323 delta = GetDeltaSeqOut(pip, lnk); 324 AddSeq(pip, lnk, delta + new_dlen - dlen); 325 326 new_len = htons(hlen + new_dlen); 327 DifferentialChecksum(&pip->ip_sum, 328 &new_len, 329 &pip->ip_len, 330 1); 331 pip->ip_len = new_len; 332 333 tc->th_sum = 0; 334 #ifdef _KERNEL 335 tc->th_x2 = 1; 336 #else 337 tc->th_sum = TcpChecksum(pip); 338 #endif 339 return (0); 340 } 341 342 /* Support the protocol used by early versions of RealPlayer */ 343 344 static int 345 alias_pna_out(struct libalias *la, struct ip *pip, 346 struct alias_link *lnk, 347 char *data, 348 int dlen) 349 { 350 struct alias_link *pna_links; 351 u_short msg_id, msg_len; 352 char *work; 353 u_short alias_port, port; 354 struct tcphdr *tc; 355 356 work = data; 357 work += 5; 358 while (work + 4 < data + dlen) { 359 memcpy(&msg_id, work, 2); 360 work += 2; 361 memcpy(&msg_len, work, 2); 362 work += 2; 363 if (ntohs(msg_id) == 0) { 364 /* end of options */ 365 return (0); 366 } 367 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 368 memcpy(&port, work, 2); 369 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 370 port, 0, IPPROTO_UDP, 1); 371 if (pna_links != NULL) { 372 #ifndef NO_FW_PUNCH 373 /* Punch hole in firewall */ 374 PunchFWHole(pna_links); 375 #endif 376 tc = (struct tcphdr *)ip_next(pip); 377 alias_port = GetAliasPort(pna_links); 378 memcpy(work, &alias_port, 2); 379 380 /* Compute TCP checksum for revised packet */ 381 tc->th_sum = 0; 382 #ifdef _KERNEL 383 tc->th_x2 = 1; 384 #else 385 tc->th_sum = TcpChecksum(pip); 386 #endif 387 } 388 } 389 work += ntohs(msg_len); 390 } 391 392 return (0); 393 } 394 395 void 396 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 397 { 398 int hlen, tlen, dlen; 399 struct tcphdr *tc; 400 char *data; 401 const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 402 const char *okstr = "OK", *client_port_str = "client_port"; 403 const char *server_port_str = "server_port"; 404 int i, parseOk; 405 406 (void)maxpacketsize; 407 408 tc = (struct tcphdr *)ip_next(pip); 409 hlen = (pip->ip_hl + tc->th_off) << 2; 410 tlen = ntohs(pip->ip_len); 411 dlen = tlen - hlen; 412 413 data = (char *)pip; 414 data += hlen; 415 416 /* When aliasing a client, check for the SETUP request */ 417 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 418 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 419 420 if (dlen >= (int)strlen(setup)) { 421 if (memcmp(data, setup, strlen(setup)) == 0) { 422 alias_rtsp_out(la, pip, lnk, data, client_port_str); 423 return; 424 } 425 } 426 if (dlen >= (int)strlen(pna)) { 427 if (memcmp(data, pna, strlen(pna)) == 0) { 428 alias_pna_out(la, pip, lnk, data, dlen); 429 } 430 } 431 } else { 432 433 /* 434 * When aliasing a server, check for the 200 reply 435 * Accomodate varying number of blanks between 200 & OK 436 */ 437 438 if (dlen >= (int)strlen(str200)) { 439 440 for (parseOk = 0, i = 0; 441 i <= dlen - (int)strlen(str200); 442 i++) { 443 if (memcmp(&data[i], str200, strlen(str200)) == 0) { 444 parseOk = 1; 445 break; 446 } 447 } 448 if (parseOk) { 449 450 i += strlen(str200); /* skip string found */ 451 while (data[i] == ' ') /* skip blank(s) */ 452 i++; 453 454 if ((dlen - i) >= (int)strlen(okstr)) { 455 456 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 457 alias_rtsp_out(la, pip, lnk, data, server_port_str); 458 459 } 460 } 461 } 462 } 463 } 464