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