alias_smedia.c revision 131699
163899Sarchie/* 263899Sarchie * alias_smedia.c 363899Sarchie * 463899Sarchie * Copyright (c) 2000 Whistle Communications, Inc. 563899Sarchie * All rights reserved. 663899Sarchie * 763899Sarchie * Subject to the following obligations and disclaimer of warranty, use and 863899Sarchie * redistribution of this software, in source or object code forms, with or 963899Sarchie * without modifications are expressly permitted by Whistle Communications; 1063899Sarchie * provided, however, that: 1163899Sarchie * 1. Any and all reproductions of the source or object code must include the 1263899Sarchie * copyright notice above and the following disclaimer of warranties; and 1363899Sarchie * 2. No rights are granted, in any manner or form, to use Whistle 1463899Sarchie * Communications, Inc. trademarks, including the mark "WHISTLE 1563899Sarchie * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1663899Sarchie * such appears in the above copyright notice or in the software. 1763899Sarchie * 1863899Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 1963899Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2063899Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2163899Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2263899Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2363899Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2463899Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2563899Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2663899Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2763899Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2863899Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 2963899Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3063899Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3163899Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3263899Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3363899Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3463899Sarchie * OF SUCH DAMAGE. 3563899Sarchie * 3663899Sarchie * Copyright (c) 2000 Junichi SATOH <junichi@astec.co.jp> 3763899Sarchie * <junichi@junichi.org> 3863899Sarchie * All rights reserved. 3963899Sarchie * 4063899Sarchie * Redistribution and use in source and binary forms, with or without 4163899Sarchie * modification, are permitted provided that the following conditions 4263899Sarchie * are met: 4363899Sarchie * 1. Redistributions of source code must retain the above copyright 4463899Sarchie * notice, this list of conditions and the following disclaimer. 4563899Sarchie * 2. Redistributions in binary form must reproduce the above copyright 4663899Sarchie * notice, this list of conditions and the following disclaimer in the 4763899Sarchie * documentation and/or other materials provided with the distribution. 4863899Sarchie * 4963899Sarchie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 5063899Sarchie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5163899Sarchie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5263899Sarchie * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 5363899Sarchie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5463899Sarchie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5563899Sarchie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5663899Sarchie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5763899Sarchie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5863899Sarchie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5963899Sarchie * SUCH DAMAGE. 6063899Sarchie * 6163899Sarchie * Authors: Erik Salander <erik@whistle.com> 6263899Sarchie * Junichi SATOH <junichi@astec.co.jp> 6363899Sarchie * <junichi@junichi.org> 6463899Sarchie */ 6563899Sarchie 6684195Sdillon#include <sys/cdefs.h> 6784195Sdillon__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_smedia.c 131699 2004-07-06 12:13:28Z des $"); 6884195Sdillon 6963899Sarchie/* 7063899Sarchie Alias_smedia.c is meant to contain the aliasing code for streaming media 7163899Sarchie protocols. It performs special processing for RSTP sessions under TCP. 7263899Sarchie Specifically, when a SETUP request is sent by a client, or a 200 reply 7399207Sbrian is sent by a server, it is intercepted and modified. The address is 7463899Sarchie changed to the gateway machine and an aliasing port is used. 7563899Sarchie 7699207Sbrian More specifically, the "client_port" configuration parameter is 7799207Sbrian parsed for SETUP requests. The "server_port" configuration parameter is 7863899Sarchie parsed for 200 replies eminating from a server. This is intended to handle 7963899Sarchie the unicast case. 8063899Sarchie 8163899Sarchie RTSP also allows a redirection of a stream to another client by using the 8263899Sarchie "destination" configuration parameter. The destination config parm would 8399207Sbrian indicate a different IP address. This function is NOT supported by the 8463899Sarchie RTSP translation code below. 8563899Sarchie 8663899Sarchie The RTSP multicast functions without any address translation intervention. 8763899Sarchie 8863899Sarchie For this routine to work, the SETUP/200 must fit entirely 8963899Sarchie into a single TCP packet. This is typically the case, but exceptions 9063899Sarchie can easily be envisioned under the actual specifications. 9163899Sarchie 9263899Sarchie Probably the most troubling aspect of the approach taken here is 9363899Sarchie that the new SETUP/200 will typically be a different length, and 9463899Sarchie this causes a certain amount of bookkeeping to keep track of the 9563899Sarchie changes of sequence and acknowledgment numbers, since the client 9663899Sarchie machine is totally unaware of the modification to the TCP stream. 9763899Sarchie 9899207Sbrian Initial version: May, 2000 (eds) 9963899Sarchie*/ 10063899Sarchie 10163899Sarchie#include <stdio.h> 10263899Sarchie#include <string.h> 10363899Sarchie#include <sys/types.h> 10463899Sarchie#include <netinet/in_systm.h> 10563899Sarchie#include <netinet/in.h> 10663899Sarchie#include <netinet/ip.h> 10763899Sarchie#include <netinet/tcp.h> 10863899Sarchie#include <netinet/udp.h> 10963899Sarchie 11063899Sarchie#include "alias_local.h" 11163899Sarchie 11299207Sbrian#define RTSP_CONTROL_PORT_NUMBER_1 554 11399207Sbrian#define RTSP_CONTROL_PORT_NUMBER_2 7070 11463899Sarchie#define RTSP_PORT_GROUP 2 11563899Sarchie 11663899Sarchie#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) 11763899Sarchie 11865892Srustatic int 11971796Sbriansearch_string(char *data, int dlen, const char *search_str) 12063899Sarchie{ 121127094Sdes int i, j, k; 122127094Sdes int search_str_len; 12363899Sarchie 124127094Sdes search_str_len = strlen(search_str); 125127094Sdes for (i = 0; i < dlen - search_str_len; i++) { 126127094Sdes for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { 127127094Sdes if (data[j] != search_str[k] && 128127094Sdes data[j] != search_str[k] - ('a' - 'A')) { 129127094Sdes break; 130127094Sdes } 131127094Sdes if (k == search_str_len - 1) { 132131613Sdes return (j + 1); 133127094Sdes } 134127094Sdes } 13563899Sarchie } 136131613Sdes return (-1); 13763899Sarchie} 13863899Sarchie 13965892Srustatic int 140124621Sphkalias_rtsp_out(struct libalias *la, struct ip *pip, 141131614Sdes struct alias_link *lnk, 142127094Sdes char *data, 143127094Sdes const char *port_str) 14463899Sarchie{ 145127094Sdes int hlen, tlen, dlen; 146127094Sdes struct tcphdr *tc; 147127094Sdes int i, j, pos, state, port_dlen, new_dlen, delta; 148127094Sdes u_short p[2], new_len; 149127094Sdes u_short sport, eport, base_port; 150127094Sdes u_short salias = 0, ealias = 0, base_alias = 0; 151127094Sdes const char *transport_str = "transport:"; 152127094Sdes char newdata[2048], *port_data, *port_newdata, stemp[80]; 153127094Sdes int links_created = 0, pkt_updated = 0; 154131614Sdes struct alias_link *rtsp_lnk = NULL; 155127094Sdes struct in_addr null_addr; 15663899Sarchie 157127094Sdes /* Calculate data length of TCP packet */ 158131699Sdes tc = (struct tcphdr *)ip_next(pip); 159127094Sdes hlen = (pip->ip_hl + tc->th_off) << 2; 160127094Sdes tlen = ntohs(pip->ip_len); 161127094Sdes dlen = tlen - hlen; 16263899Sarchie 163127094Sdes /* Find keyword, "Transport: " */ 164127094Sdes pos = search_string(data, dlen, transport_str); 16563899Sarchie if (pos < 0) { 166131613Sdes return (-1); 16763899Sarchie } 168127094Sdes port_data = data + pos; 169127094Sdes port_dlen = dlen - pos; 17063899Sarchie 171127094Sdes memcpy(newdata, data, pos); 172127094Sdes port_newdata = newdata + pos; 17363899Sarchie 174131614Sdes while (port_dlen > (int)strlen(port_str)) { 175127094Sdes /* Find keyword, appropriate port string */ 176127094Sdes pos = search_string(port_data, port_dlen, port_str); 177127094Sdes if (pos < 0) { 178127094Sdes break; 17963899Sarchie } 180127094Sdes memcpy(port_newdata, port_data, pos + 1); 181127094Sdes port_newdata += (pos + 1); 18263899Sarchie 183127094Sdes p[0] = p[1] = 0; 184127094Sdes sport = eport = 0; 185127094Sdes state = 0; 186127094Sdes for (i = pos; i < port_dlen; i++) { 187127094Sdes switch (state) { 188127094Sdes case 0: 189127094Sdes if (port_data[i] == '=') { 190127094Sdes state++; 191127094Sdes } 192127094Sdes break; 193127094Sdes case 1: 194127094Sdes if (ISDIGIT(port_data[i])) { 195127094Sdes p[0] = p[0] * 10 + port_data[i] - '0'; 196127094Sdes } else { 197127094Sdes if (port_data[i] == ';') { 198127094Sdes state = 3; 199127094Sdes } 200127094Sdes if (port_data[i] == '-') { 201127094Sdes state++; 202127094Sdes } 203127094Sdes } 204127094Sdes break; 205127094Sdes case 2: 206127094Sdes if (ISDIGIT(port_data[i])) { 207127094Sdes p[1] = p[1] * 10 + port_data[i] - '0'; 208127094Sdes } else { 209127094Sdes state++; 210127094Sdes } 211127094Sdes break; 212127094Sdes case 3: 213127094Sdes base_port = p[0]; 214127094Sdes sport = htons(p[0]); 215127094Sdes eport = htons(p[1]); 21663899Sarchie 217127094Sdes if (!links_created) { 218127094Sdes 219127094Sdes links_created = 1; 220127094Sdes /* 221127094Sdes * Find an even numbered port 222127094Sdes * number base that satisfies the 223127094Sdes * contiguous number of ports we 224127094Sdes * need 225127094Sdes */ 226127094Sdes null_addr.s_addr = 0; 227127094Sdes if (0 == (salias = FindNewPortGroup(la, null_addr, 228127094Sdes FindAliasAddress(la, pip->ip_src), 229127094Sdes sport, 0, 230127094Sdes RTSP_PORT_GROUP, 231127094Sdes IPPROTO_UDP, 1))) { 23263899Sarchie#ifdef DEBUG 233127094Sdes fprintf(stderr, 234127094Sdes "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 23563899Sarchie#endif 236127094Sdes } else { 23763899Sarchie 238127094Sdes base_alias = ntohs(salias); 239127094Sdes for (j = 0; j < RTSP_PORT_GROUP; j++) { 240127094Sdes /* 241127094Sdes * Establish link 242127094Sdes * to port found in 243127094Sdes * RTSP packet 244127094Sdes */ 245131614Sdes rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, 246127094Sdes htons(base_port + j), htons(base_alias + j), 247127094Sdes IPPROTO_UDP); 248131614Sdes if (rtsp_lnk != NULL) { 24963899Sarchie#ifndef NO_FW_PUNCH 250127094Sdes /* 251127094Sdes * Punch 252127094Sdes * hole in 253127094Sdes * firewall 254127094Sdes */ 255131614Sdes PunchFWHole(rtsp_lnk); 25663899Sarchie#endif 257127094Sdes } else { 25863899Sarchie#ifdef DEBUG 259127094Sdes fprintf(stderr, 260127094Sdes "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); 26163899Sarchie#endif 262127094Sdes break; 263127094Sdes } 264127094Sdes } 265127094Sdes } 266127094Sdes ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); 267127094Sdes } 268131614Sdes if (salias && rtsp_lnk) { 26963899Sarchie 270127094Sdes pkt_updated = 1; 27163899Sarchie 272127094Sdes /* Copy into IP packet */ 273127094Sdes sprintf(stemp, "%d", ntohs(salias)); 274127094Sdes memcpy(port_newdata, stemp, strlen(stemp)); 275127094Sdes port_newdata += strlen(stemp); 27663899Sarchie 277127094Sdes if (eport != 0) { 278127094Sdes *port_newdata = '-'; 279127094Sdes port_newdata++; 28063899Sarchie 281127094Sdes /* Copy into IP packet */ 282127094Sdes sprintf(stemp, "%d", ntohs(ealias)); 283127094Sdes memcpy(port_newdata, stemp, strlen(stemp)); 284127094Sdes port_newdata += strlen(stemp); 285127094Sdes } 286127094Sdes *port_newdata = ';'; 287127094Sdes port_newdata++; 288127094Sdes } 289127094Sdes state++; 290127094Sdes break; 291127094Sdes } 292127094Sdes if (state > 3) { 293127094Sdes break; 294127094Sdes } 29563899Sarchie } 296127094Sdes port_data += i; 297127094Sdes port_dlen -= i; 29863899Sarchie } 29963899Sarchie 300127094Sdes if (!pkt_updated) 301131613Sdes return (-1); 30263899Sarchie 303127094Sdes memcpy(port_newdata, port_data, port_dlen); 304127094Sdes port_newdata += port_dlen; 305127094Sdes *port_newdata = '\0'; 30663899Sarchie 307127094Sdes /* Create new packet */ 308127094Sdes new_dlen = port_newdata - newdata; 309127094Sdes memcpy(data, newdata, new_dlen); 31063899Sarchie 311131614Sdes SetAckModified(lnk); 312131614Sdes delta = GetDeltaSeqOut(pip, lnk); 313131614Sdes AddSeq(pip, lnk, delta + new_dlen - dlen); 31463899Sarchie 315127094Sdes new_len = htons(hlen + new_dlen); 316127094Sdes DifferentialChecksum(&pip->ip_sum, 317127094Sdes &new_len, 318127094Sdes &pip->ip_len, 319127094Sdes 1); 320127094Sdes pip->ip_len = new_len; 32163899Sarchie 322127094Sdes tc->th_sum = 0; 323127094Sdes tc->th_sum = TcpChecksum(pip); 32463899Sarchie 325131613Sdes return (0); 32663899Sarchie} 32763899Sarchie 32863899Sarchie/* Support the protocol used by early versions of RealPlayer */ 32963899Sarchie 33065892Srustatic int 331124621Sphkalias_pna_out(struct libalias *la, struct ip *pip, 332131614Sdes struct alias_link *lnk, 333127094Sdes char *data, 334127094Sdes int dlen) 33563899Sarchie{ 336127094Sdes struct alias_link *pna_links; 337127094Sdes u_short msg_id, msg_len; 338127094Sdes char *work; 339127094Sdes u_short alias_port, port; 340127094Sdes struct tcphdr *tc; 34163899Sarchie 342127094Sdes work = data; 343127094Sdes work += 5; 344127094Sdes while (work + 4 < data + dlen) { 345127094Sdes memcpy(&msg_id, work, 2); 346127094Sdes work += 2; 347127094Sdes memcpy(&msg_len, work, 2); 348127094Sdes work += 2; 349127094Sdes if (ntohs(msg_id) == 0) { 350127094Sdes /* end of options */ 351131613Sdes return (0); 352127094Sdes } 353127094Sdes if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 354127094Sdes memcpy(&port, work, 2); 355131614Sdes pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 356127094Sdes port, 0, IPPROTO_UDP, 1); 357127094Sdes if (pna_links != NULL) { 35863899Sarchie#ifndef NO_FW_PUNCH 359127094Sdes /* Punch hole in firewall */ 360127094Sdes PunchFWHole(pna_links); 36163899Sarchie#endif 362131699Sdes tc = (struct tcphdr *)ip_next(pip); 363127094Sdes alias_port = GetAliasPort(pna_links); 364127094Sdes memcpy(work, &alias_port, 2); 36563899Sarchie 366127094Sdes /* Compute TCP checksum for revised packet */ 367127094Sdes tc->th_sum = 0; 368127094Sdes tc->th_sum = TcpChecksum(pip); 369127094Sdes } 370127094Sdes } 371127094Sdes work += ntohs(msg_len); 37263899Sarchie } 37399207Sbrian 374131613Sdes return (0); 37563899Sarchie} 37663899Sarchie 37763899Sarchievoid 378131614SdesAliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 37963899Sarchie{ 380127094Sdes int hlen, tlen, dlen; 381127094Sdes struct tcphdr *tc; 382127094Sdes char *data; 383127094Sdes const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 384127094Sdes const char *okstr = "OK", *client_port_str = "client_port"; 385127094Sdes const char *server_port_str = "server_port"; 386127094Sdes int i, parseOk; 38763899Sarchie 388131614Sdes (void)maxpacketsize; 389131614Sdes 390131699Sdes tc = (struct tcphdr *)ip_next(pip); 391127094Sdes hlen = (pip->ip_hl + tc->th_off) << 2; 392127094Sdes tlen = ntohs(pip->ip_len); 393127094Sdes dlen = tlen - hlen; 39463899Sarchie 395127094Sdes data = (char *)pip; 396127094Sdes data += hlen; 39763899Sarchie 398127094Sdes /* When aliasing a client, check for the SETUP request */ 399127094Sdes if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 400127094Sdes (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 40163899Sarchie 402131614Sdes if (dlen >= (int)strlen(setup)) { 403127094Sdes if (memcmp(data, setup, strlen(setup)) == 0) { 404131614Sdes alias_rtsp_out(la, pip, lnk, data, client_port_str); 405127094Sdes return; 406127094Sdes } 407127094Sdes } 408131614Sdes if (dlen >= (int)strlen(pna)) { 409127094Sdes if (memcmp(data, pna, strlen(pna)) == 0) { 410131614Sdes alias_pna_out(la, pip, lnk, data, dlen); 411127094Sdes } 412127094Sdes } 413127094Sdes } else { 41463899Sarchie 415127094Sdes /* 416127094Sdes * When aliasing a server, check for the 200 reply 417127094Sdes * Accomodate varying number of blanks between 200 & OK 418127094Sdes */ 41963899Sarchie 420131614Sdes if (dlen >= (int)strlen(str200)) { 42163899Sarchie 422127094Sdes for (parseOk = 0, i = 0; 423131614Sdes i <= dlen - (int)strlen(str200); 424127094Sdes i++) { 425127094Sdes if (memcmp(&data[i], str200, strlen(str200)) == 0) { 426127094Sdes parseOk = 1; 427127094Sdes break; 428127094Sdes } 429127094Sdes } 430127094Sdes if (parseOk) { 43163899Sarchie 432127094Sdes i += strlen(str200); /* skip string found */ 433127094Sdes while (data[i] == ' ') /* skip blank(s) */ 434127094Sdes i++; 43563899Sarchie 436131614Sdes if ((dlen - i) >= (int)strlen(okstr)) { 43799207Sbrian 438127094Sdes if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 439131614Sdes alias_rtsp_out(la, pip, lnk, data, server_port_str); 44063899Sarchie 441127094Sdes } 442127094Sdes } 443127094Sdes } 444127094Sdes } 44563899Sarchie} 446