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 #include <stdio.h> 102 #include <string.h> 103 #include <sys/types.h> 104 #include <netinet/in_systm.h> 105 #include <netinet/in.h> 106 #include <netinet/ip.h> 107 #include <netinet/tcp.h> 108 #include <netinet/udp.h> 109 110 #include "alias_local.h" 111 112 #define RTSP_CONTROL_PORT_NUMBER_1 554 113 #define RTSP_CONTROL_PORT_NUMBER_2 7070 114 #define RTSP_PORT_GROUP 2 115 116 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) 117 118 static int 119 search_string(char *data, int dlen, const char *search_str) 120 { 121 int i, j, k; 122 int search_str_len; 123 124 search_str_len = strlen(search_str); 125 for (i = 0; i < dlen - search_str_len; i++) { 126 for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { 127 if (data[j] != search_str[k] && 128 data[j] != search_str[k] - ('a' - 'A')) { 129 break; 130 } 131 if (k == search_str_len - 1) { 132 return (j + 1); 133 } 134 } 135 } 136 return (-1); 137 } 138 139 static int 140 alias_rtsp_out(struct libalias *la, struct ip *pip, 141 struct alias_link *lnk, 142 char *data, 143 const char *port_str) 144 { 145 int hlen, tlen, dlen; 146 struct tcphdr *tc; 147 int i, j, pos, state, port_dlen, new_dlen, delta; 148 u_short p[2], new_len; 149 u_short sport, eport, base_port; 150 u_short salias = 0, ealias = 0, base_alias = 0; 151 const char *transport_str = "transport:"; 152 char newdata[2048], *port_data, *port_newdata, stemp[80]; 153 int links_created = 0, pkt_updated = 0; 154 struct alias_link *rtsp_lnk = NULL; 155 struct in_addr null_addr; 156 157 /* Calculate data length of TCP packet */ 158 tc = (struct tcphdr *)ip_next(pip); 159 hlen = (pip->ip_hl + tc->th_off) << 2; 160 tlen = ntohs(pip->ip_len); 161 dlen = tlen - hlen; 162 163 /* Find keyword, "Transport: " */ 164 pos = search_string(data, dlen, transport_str); 165 if (pos < 0) { 166 return (-1); 167 } 168 port_data = data + pos; 169 port_dlen = dlen - pos; 170 171 memcpy(newdata, data, pos); 172 port_newdata = newdata + pos; 173 174 while (port_dlen > (int)strlen(port_str)) { 175 /* Find keyword, appropriate port string */ 176 pos = search_string(port_data, port_dlen, port_str); 177 if (pos < 0) { 178 break; 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 /* 221 * Find an even numbered port 222 * number base that satisfies the 223 * contiguous number of ports we 224 * need 225 */ 226 null_addr.s_addr = 0; 227 if (0 == (salias = FindNewPortGroup(la, null_addr, 228 FindAliasAddress(la, pip->ip_src), 229 sport, 0, 230 RTSP_PORT_GROUP, 231 IPPROTO_UDP, 1))) { 232 #ifdef DEBUG 233 fprintf(stderr, 234 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 235 #endif 236 } else { 237 238 base_alias = ntohs(salias); 239 for (j = 0; j < RTSP_PORT_GROUP; j++) { 240 /* 241 * Establish link 242 * to port found in 243 * RTSP packet 244 */ 245 rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, 246 htons(base_port + j), htons(base_alias + j), 247 IPPROTO_UDP); 248 if (rtsp_lnk != NULL) { 249 #ifndef NO_FW_PUNCH 250 /* 251 * Punch 252 * hole in 253 * firewall 254 */ 255 PunchFWHole(rtsp_lnk); 256 #endif 257 } else { 258 #ifdef DEBUG 259 fprintf(stderr, 260 "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); 261 #endif 262 break; 263 } 264 } 265 } 266 ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); 267 } 268 if (salias && rtsp_lnk) { 269 270 pkt_updated = 1; 271 272 /* Copy into IP packet */ 273 sprintf(stemp, "%d", ntohs(salias)); 274 memcpy(port_newdata, stemp, strlen(stemp)); 275 port_newdata += strlen(stemp); 276 277 if (eport != 0) { 278 *port_newdata = '-'; 279 port_newdata++; 280 281 /* Copy into IP packet */ 282 sprintf(stemp, "%d", ntohs(ealias)); 283 memcpy(port_newdata, stemp, strlen(stemp)); 284 port_newdata += strlen(stemp); 285 } 286 *port_newdata = ';'; 287 port_newdata++; 288 } 289 state++; 290 break; 291 } 292 if (state > 3) { 293 break; 294 } 295 } 296 port_data += i; 297 port_dlen -= i; 298 } 299 300 if (!pkt_updated) 301 return (-1); 302 303 memcpy(port_newdata, port_data, port_dlen); 304 port_newdata += port_dlen; 305 *port_newdata = '\0'; 306 307 /* Create new packet */ 308 new_dlen = port_newdata - newdata; 309 memcpy(data, newdata, new_dlen); 310 311 SetAckModified(lnk); 312 delta = GetDeltaSeqOut(pip, lnk); 313 AddSeq(pip, lnk, delta + new_dlen - dlen); 314 315 new_len = htons(hlen + new_dlen); 316 DifferentialChecksum(&pip->ip_sum, 317 &new_len, 318 &pip->ip_len, 319 1); 320 pip->ip_len = new_len; 321 322 tc->th_sum = 0; 323 tc->th_sum = TcpChecksum(pip); 324 325 return (0); 326 } 327 328 /* Support the protocol used by early versions of RealPlayer */ 329 330 static int 331 alias_pna_out(struct libalias *la, struct ip *pip, 332 struct alias_link *lnk, 333 char *data, 334 int dlen) 335 { 336 struct alias_link *pna_links; 337 u_short msg_id, msg_len; 338 char *work; 339 u_short alias_port, port; 340 struct tcphdr *tc; 341 342 work = data; 343 work += 5; 344 while (work + 4 < data + dlen) { 345 memcpy(&msg_id, work, 2); 346 work += 2; 347 memcpy(&msg_len, work, 2); 348 work += 2; 349 if (ntohs(msg_id) == 0) { 350 /* end of options */ 351 return (0); 352 } 353 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 354 memcpy(&port, work, 2); 355 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 356 port, 0, IPPROTO_UDP, 1); 357 if (pna_links != NULL) { 358 #ifndef NO_FW_PUNCH 359 /* Punch hole in firewall */ 360 PunchFWHole(pna_links); 361 #endif 362 tc = (struct tcphdr *)ip_next(pip); 363 alias_port = GetAliasPort(pna_links); 364 memcpy(work, &alias_port, 2); 365 366 /* Compute TCP checksum for revised packet */ 367 tc->th_sum = 0; 368 tc->th_sum = TcpChecksum(pip); 369 } 370 } 371 work += ntohs(msg_len); 372 } 373 374 return (0); 375 } 376 377 void 378 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 379 { 380 int hlen, tlen, dlen; 381 struct tcphdr *tc; 382 char *data; 383 const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 384 const char *okstr = "OK", *client_port_str = "client_port"; 385 const char *server_port_str = "server_port"; 386 int i, parseOk; 387 388 (void)maxpacketsize; 389 390 tc = (struct tcphdr *)ip_next(pip); 391 hlen = (pip->ip_hl + tc->th_off) << 2; 392 tlen = ntohs(pip->ip_len); 393 dlen = tlen - hlen; 394 395 data = (char *)pip; 396 data += hlen; 397 398 /* When aliasing a client, check for the SETUP request */ 399 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 400 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 401 402 if (dlen >= (int)strlen(setup)) { 403 if (memcmp(data, setup, strlen(setup)) == 0) { 404 alias_rtsp_out(la, pip, lnk, data, client_port_str); 405 return; 406 } 407 } 408 if (dlen >= (int)strlen(pna)) { 409 if (memcmp(data, pna, strlen(pna)) == 0) { 410 alias_pna_out(la, pip, lnk, data, dlen); 411 } 412 } 413 } else { 414 415 /* 416 * When aliasing a server, check for the 200 reply 417 * Accomodate varying number of blanks between 200 & OK 418 */ 419 420 if (dlen >= (int)strlen(str200)) { 421 422 for (parseOk = 0, i = 0; 423 i <= dlen - (int)strlen(str200); 424 i++) { 425 if (memcmp(&data[i], str200, strlen(str200)) == 0) { 426 parseOk = 1; 427 break; 428 } 429 } 430 if (parseOk) { 431 432 i += strlen(str200); /* skip string found */ 433 while (data[i] == ' ') /* skip blank(s) */ 434 i++; 435 436 if ((dlen - i) >= (int)strlen(okstr)) { 437 438 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 439 alias_rtsp_out(la, pip, lnk, data, server_port_str); 440 441 } 442 } 443 } 444 } 445 } 446