alias_smedia.c revision 145921
133965Sjdp/* 277298Sobrien * alias_smedia.c 333965Sjdp * 433965Sjdp * Copyright (c) 2000 Whistle Communications, Inc. 533965Sjdp * All rights reserved. 633965Sjdp * 733965Sjdp * Subject to the following obligations and disclaimer of warranty, use and 833965Sjdp * redistribution of this software, in source or object code forms, with or 933965Sjdp * without modifications are expressly permitted by Whistle Communications; 1033965Sjdp * provided, however, that: 1133965Sjdp * 1. Any and all reproductions of the source or object code must include the 1233965Sjdp * copyright notice above and the following disclaimer of warranties; and 1333965Sjdp * 2. No rights are granted, in any manner or form, to use Whistle 1433965Sjdp * Communications, Inc. trademarks, including the mark "WHISTLE 1533965Sjdp * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1633965Sjdp * such appears in the above copyright notice or in the software. 1733965Sjdp * 1833965Sjdp * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 1933965Sjdp * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2077298Sobrien * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2133965Sjdp * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2277298Sobrien * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2333965Sjdp * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2433965Sjdp * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2533965Sjdp * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2633965Sjdp * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2733965Sjdp * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2833965Sjdp * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 2933965Sjdp * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3033965Sjdp * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3133965Sjdp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3233965Sjdp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3333965Sjdp * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3433965Sjdp * OF SUCH DAMAGE. 3533965Sjdp * 3633965Sjdp * Copyright (c) 2000 Junichi SATOH <junichi@astec.co.jp> 3733965Sjdp * <junichi@junichi.org> 3833965Sjdp * All rights reserved. 3933965Sjdp * 4033965Sjdp * Redistribution and use in source and binary forms, with or without 4133965Sjdp * modification, are permitted provided that the following conditions 4233965Sjdp * are met: 4377298Sobrien * 1. Redistributions of source code must retain the above copyright 4460484Sobrien * notice, this list of conditions and the following disclaimer. 4560484Sobrien * 2. Redistributions in binary form must reproduce the above copyright 4660484Sobrien * notice, this list of conditions and the following disclaimer in the 4760484Sobrien * documentation and/or other materials provided with the distribution. 4833965Sjdp * 4933965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 5033965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5133965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5233965Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 5333965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5433965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5533965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5633965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5733965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5833965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5933965Sjdp * SUCH DAMAGE. 6033965Sjdp * 6133965Sjdp * Authors: Erik Salander <erik@whistle.com> 6233965Sjdp * Junichi SATOH <junichi@astec.co.jp> 6333965Sjdp * <junichi@junichi.org> 6477298Sobrien */ 6577298Sobrien 6633965Sjdp#include <sys/cdefs.h> 6777298Sobrien__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_smedia.c 145921 2005-05-05 19:27:32Z glebius $"); 6877298Sobrien 6933965Sjdp/* 7038889Sjdp Alias_smedia.c is meant to contain the aliasing code for streaming media 7138889Sjdp protocols. It performs special processing for RSTP sessions under TCP. 7277298Sobrien Specifically, when a SETUP request is sent by a client, or a 200 reply 7338889Sjdp is sent by a server, it is intercepted and modified. The address is 7433965Sjdp changed to the gateway machine and an aliasing port is used. 7533965Sjdp 7633965Sjdp More specifically, the "client_port" configuration parameter is 7777298Sobrien parsed for SETUP requests. The "server_port" configuration parameter is 7877298Sobrien parsed for 200 replies eminating from a server. This is intended to handle 7933965Sjdp the unicast case. 8033965Sjdp 8133965Sjdp RTSP also allows a redirection of a stream to another client by using the 8233965Sjdp "destination" configuration parameter. The destination config parm would 8333965Sjdp indicate a different IP address. This function is NOT supported by the 8438889Sjdp RTSP translation code below. 8538889Sjdp 8638889Sjdp The RTSP multicast functions without any address translation intervention. 8733965Sjdp 8833965Sjdp For this routine to work, the SETUP/200 must fit entirely 8933965Sjdp into a single TCP packet. This is typically the case, but exceptions 9033965Sjdp can easily be envisioned under the actual specifications. 9133965Sjdp 9233965Sjdp Probably the most troubling aspect of the approach taken here is 9333965Sjdp that the new SETUP/200 will typically be a different length, and 9433965Sjdp this causes a certain amount of bookkeeping to keep track of the 9577298Sobrien changes of sequence and acknowledgment numbers, since the client 9633965Sjdp machine is totally unaware of the modification to the TCP stream. 9733965Sjdp 9833965Sjdp Initial version: May, 2000 (eds) 9933965Sjdp*/ 10033965Sjdp 10133965Sjdp#ifdef _KERNEL 10233965Sjdp#include <sys/param.h> 10377298Sobrien#include <sys/libkern.h> 10433965Sjdp#else 10577298Sobrien#include <sys/types.h> 10633965Sjdp#include <stdio.h> 10733965Sjdp#include <string.h> 10833965Sjdp#endif 10933965Sjdp 11033965Sjdp#include <netinet/in_systm.h> 11133965Sjdp#include <netinet/in.h> 11233965Sjdp#include <netinet/ip.h> 11333965Sjdp#include <netinet/tcp.h> 11433965Sjdp#include <netinet/udp.h> 11533965Sjdp 11633965Sjdp#ifdef _KERNEL 11760484Sobrien#include <netinet/libalias/alias_local.h> 11877298Sobrien#include <netinet/libalias/alias.h> 11933965Sjdp#else 12033965Sjdp#include "alias_local.h" 12133965Sjdp#endif 12233965Sjdp 12333965Sjdp#define RTSP_CONTROL_PORT_NUMBER_1 554 12433965Sjdp#define RTSP_CONTROL_PORT_NUMBER_2 7070 12533965Sjdp#define RTSP_PORT_GROUP 2 12633965Sjdp 12733965Sjdp#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) 12833965Sjdp 12933965Sjdpstatic int 13033965Sjdpsearch_string(char *data, int dlen, const char *search_str) 13133965Sjdp{ 13233965Sjdp int i, j, k; 13333965Sjdp int search_str_len; 13433965Sjdp 13533965Sjdp search_str_len = strlen(search_str); 13633965Sjdp for (i = 0; i < dlen - search_str_len; i++) { 13733965Sjdp for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { 13833965Sjdp if (data[j] != search_str[k] && 13933965Sjdp data[j] != search_str[k] - ('a' - 'A')) { 14033965Sjdp break; 14133965Sjdp } 14233965Sjdp if (k == search_str_len - 1) { 14333965Sjdp return (j + 1); 14477298Sobrien } 14533965Sjdp } 14633965Sjdp } 14760484Sobrien return (-1); 14833965Sjdp} 14933965Sjdp 15033965Sjdpstatic int 15133965Sjdpalias_rtsp_out(struct libalias *la, struct ip *pip, 15233965Sjdp struct alias_link *lnk, 15333965Sjdp char *data, 15433965Sjdp const char *port_str) 15533965Sjdp{ 15633965Sjdp int hlen, tlen, dlen; 15733965Sjdp struct tcphdr *tc; 15833965Sjdp int i, j, pos, state, port_dlen, new_dlen, delta; 15933965Sjdp u_short p[2], new_len; 16033965Sjdp u_short sport, eport, base_port; 16133965Sjdp u_short salias = 0, ealias = 0, base_alias = 0; 16260484Sobrien const char *transport_str = "transport:"; 16333965Sjdp char newdata[2048], *port_data, *port_newdata, stemp[80]; 16433965Sjdp int links_created = 0, pkt_updated = 0; 16533965Sjdp struct alias_link *rtsp_lnk = NULL; 16633965Sjdp struct in_addr null_addr; 16733965Sjdp 16833965Sjdp /* Calculate data length of TCP packet */ 16933965Sjdp tc = (struct tcphdr *)ip_next(pip); 17033965Sjdp hlen = (pip->ip_hl + tc->th_off) << 2; 17133965Sjdp tlen = ntohs(pip->ip_len); 17233965Sjdp dlen = tlen - hlen; 17333965Sjdp 17433965Sjdp /* Find keyword, "Transport: " */ 17533965Sjdp pos = search_string(data, dlen, transport_str); 17633965Sjdp if (pos < 0) { 17733965Sjdp return (-1); 17833965Sjdp } 17933965Sjdp port_data = data + pos; 18033965Sjdp port_dlen = dlen - pos; 18133965Sjdp 18233965Sjdp memcpy(newdata, data, pos); 18333965Sjdp port_newdata = newdata + pos; 18433965Sjdp 18533965Sjdp while (port_dlen > (int)strlen(port_str)) { 18633965Sjdp /* Find keyword, appropriate port string */ 18733965Sjdp pos = search_string(port_data, port_dlen, port_str); 18833965Sjdp if (pos < 0) { 18933965Sjdp break; 19033965Sjdp } 19133965Sjdp memcpy(port_newdata, port_data, pos + 1); 19233965Sjdp port_newdata += (pos + 1); 19333965Sjdp 19433965Sjdp p[0] = p[1] = 0; 19533965Sjdp sport = eport = 0; 19633965Sjdp state = 0; 19733965Sjdp for (i = pos; i < port_dlen; i++) { 19833965Sjdp switch (state) { 19933965Sjdp case 0: 20060484Sobrien if (port_data[i] == '=') { 20160484Sobrien state++; 20260484Sobrien } 20360484Sobrien break; 20460484Sobrien case 1: 20560484Sobrien if (ISDIGIT(port_data[i])) { 20660484Sobrien p[0] = p[0] * 10 + port_data[i] - '0'; 20760484Sobrien } else { 20860484Sobrien if (port_data[i] == ';') { 20960484Sobrien state = 3; 21060484Sobrien } 21160484Sobrien if (port_data[i] == '-') { 21260484Sobrien state++; 21360484Sobrien } 21460484Sobrien } 21560484Sobrien break; 21660484Sobrien case 2: 21760484Sobrien if (ISDIGIT(port_data[i])) { 21860484Sobrien p[1] = p[1] * 10 + port_data[i] - '0'; 21960484Sobrien } else { 22060484Sobrien state++; 22160484Sobrien } 22260484Sobrien break; 22360484Sobrien case 3: 22460484Sobrien base_port = p[0]; 22560484Sobrien sport = htons(p[0]); 22660484Sobrien eport = htons(p[1]); 22760484Sobrien 22860484Sobrien if (!links_created) { 22960484Sobrien 23060484Sobrien links_created = 1; 23160484Sobrien /* 23260484Sobrien * Find an even numbered port 23360484Sobrien * number base that satisfies the 23460484Sobrien * contiguous number of ports we 23560484Sobrien * need 23660484Sobrien */ 23760484Sobrien null_addr.s_addr = 0; 23860484Sobrien if (0 == (salias = FindNewPortGroup(la, null_addr, 23960484Sobrien FindAliasAddress(la, pip->ip_src), 24060484Sobrien sport, 0, 24160484Sobrien RTSP_PORT_GROUP, 24260484Sobrien IPPROTO_UDP, 1))) { 24360484Sobrien#ifdef DEBUG 24460484Sobrien fprintf(stderr, 24560484Sobrien "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 24660484Sobrien#endif 24760484Sobrien } else { 24877298Sobrien 24960484Sobrien base_alias = ntohs(salias); 25060484Sobrien for (j = 0; j < RTSP_PORT_GROUP; j++) { 25160484Sobrien /* 25260484Sobrien * Establish link 25377298Sobrien * to port found in 25460484Sobrien * RTSP packet 25560484Sobrien */ 25660484Sobrien rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, 25760484Sobrien htons(base_port + j), htons(base_alias + j), 25860484Sobrien IPPROTO_UDP); 25960484Sobrien if (rtsp_lnk != NULL) { 26060484Sobrien#ifndef NO_FW_PUNCH 26160484Sobrien /* 26260484Sobrien * Punch 26360484Sobrien * hole in 26460484Sobrien * firewall 26560484Sobrien */ 26660484Sobrien PunchFWHole(rtsp_lnk); 26760484Sobrien#endif 26877298Sobrien } else { 26977298Sobrien#ifdef DEBUG 27060484Sobrien fprintf(stderr, 27160484Sobrien "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); 27260484Sobrien#endif 27360484Sobrien break; 27460484Sobrien } 27560484Sobrien } 27660484Sobrien } 27760484Sobrien ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); 27860484Sobrien } 27960484Sobrien if (salias && rtsp_lnk) { 28060484Sobrien 28160484Sobrien pkt_updated = 1; 28260484Sobrien 28360484Sobrien /* Copy into IP packet */ 28460484Sobrien sprintf(stemp, "%d", ntohs(salias)); 28560484Sobrien memcpy(port_newdata, stemp, strlen(stemp)); 28660484Sobrien port_newdata += strlen(stemp); 28760484Sobrien 28860484Sobrien if (eport != 0) { 28960484Sobrien *port_newdata = '-'; 29060484Sobrien port_newdata++; 29160484Sobrien 29260484Sobrien /* Copy into IP packet */ 29360484Sobrien sprintf(stemp, "%d", ntohs(ealias)); 29460484Sobrien memcpy(port_newdata, stemp, strlen(stemp)); 29560484Sobrien port_newdata += strlen(stemp); 29660484Sobrien } 29760484Sobrien *port_newdata = ';'; 29860484Sobrien port_newdata++; 29960484Sobrien } 30060484Sobrien state++; 30160484Sobrien break; 30260484Sobrien } 30360484Sobrien if (state > 3) { 30460484Sobrien break; 30560484Sobrien } 30660484Sobrien } 30760484Sobrien port_data += i; 30860484Sobrien port_dlen -= i; 30960484Sobrien } 31060484Sobrien 31160484Sobrien if (!pkt_updated) 31260484Sobrien return (-1); 31360484Sobrien 31460484Sobrien memcpy(port_newdata, port_data, port_dlen); 31560484Sobrien port_newdata += port_dlen; 31660484Sobrien *port_newdata = '\0'; 31760484Sobrien 31860484Sobrien /* Create new packet */ 31960484Sobrien new_dlen = port_newdata - newdata; 32060484Sobrien memcpy(data, newdata, new_dlen); 32160484Sobrien 32260484Sobrien SetAckModified(lnk); 32360484Sobrien delta = GetDeltaSeqOut(pip, lnk); 32460484Sobrien AddSeq(pip, lnk, delta + new_dlen - dlen); 32560484Sobrien 32660484Sobrien new_len = htons(hlen + new_dlen); 32760484Sobrien DifferentialChecksum(&pip->ip_sum, 32860484Sobrien &new_len, 32960484Sobrien &pip->ip_len, 33077298Sobrien 1); 33177298Sobrien pip->ip_len = new_len; 33277298Sobrien 33377298Sobrien tc->th_sum = 0; 33477298Sobrien tc->th_sum = TcpChecksum(pip); 33533965Sjdp 33677298Sobrien return (0); 33777298Sobrien} 33877298Sobrien 33933965Sjdp/* Support the protocol used by early versions of RealPlayer */ 34033965Sjdp 34133965Sjdpstatic int 34233965Sjdpalias_pna_out(struct libalias *la, struct ip *pip, 34333965Sjdp struct alias_link *lnk, 34433965Sjdp char *data, 34533965Sjdp int dlen) 34633965Sjdp{ 34733965Sjdp struct alias_link *pna_links; 34833965Sjdp u_short msg_id, msg_len; 34933965Sjdp char *work; 35033965Sjdp u_short alias_port, port; 35133965Sjdp struct tcphdr *tc; 35233965Sjdp 35333965Sjdp work = data; 35477298Sobrien work += 5; 35577298Sobrien while (work + 4 < data + dlen) { 35633965Sjdp memcpy(&msg_id, work, 2); 35777298Sobrien work += 2; 35877298Sobrien memcpy(&msg_len, work, 2); 35933965Sjdp work += 2; 36077298Sobrien if (ntohs(msg_id) == 0) { 36133965Sjdp /* end of options */ 36277298Sobrien return (0); 36377298Sobrien } 36477298Sobrien if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 36533965Sjdp memcpy(&port, work, 2); 36677298Sobrien pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 36777298Sobrien port, 0, IPPROTO_UDP, 1); 36877298Sobrien if (pna_links != NULL) { 36977298Sobrien#ifndef NO_FW_PUNCH 37077298Sobrien /* Punch hole in firewall */ 37133965Sjdp PunchFWHole(pna_links); 37233965Sjdp#endif 37333965Sjdp tc = (struct tcphdr *)ip_next(pip); 37433965Sjdp alias_port = GetAliasPort(pna_links); 37533965Sjdp memcpy(work, &alias_port, 2); 37633965Sjdp 37738889Sjdp /* Compute TCP checksum for revised packet */ 37833965Sjdp tc->th_sum = 0; 37933965Sjdp tc->th_sum = TcpChecksum(pip); 38033965Sjdp } 38133965Sjdp } 38233965Sjdp work += ntohs(msg_len); 38333965Sjdp } 38433965Sjdp 38533965Sjdp return (0); 38633965Sjdp} 38733965Sjdp 38833965Sjdpvoid 38933965SjdpAliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 39033965Sjdp{ 39133965Sjdp int hlen, tlen, dlen; 39233965Sjdp struct tcphdr *tc; 39333965Sjdp char *data; 39433965Sjdp const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 39533965Sjdp const char *okstr = "OK", *client_port_str = "client_port"; 39633965Sjdp const char *server_port_str = "server_port"; 39733965Sjdp int i, parseOk; 39833965Sjdp 39933965Sjdp (void)maxpacketsize; 40038889Sjdp 40138889Sjdp tc = (struct tcphdr *)ip_next(pip); 40238889Sjdp hlen = (pip->ip_hl + tc->th_off) << 2; 40338889Sjdp tlen = ntohs(pip->ip_len); 40438889Sjdp dlen = tlen - hlen; 40538889Sjdp 40638889Sjdp data = (char *)pip; 40738889Sjdp data += hlen; 40838889Sjdp 40938889Sjdp /* When aliasing a client, check for the SETUP request */ 41038889Sjdp if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 41138889Sjdp (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 41238889Sjdp 41338889Sjdp if (dlen >= (int)strlen(setup)) { 41438889Sjdp if (memcmp(data, setup, strlen(setup)) == 0) { 41538889Sjdp alias_rtsp_out(la, pip, lnk, data, client_port_str); 41660484Sobrien return; 41760484Sobrien } 41860484Sobrien } 41960484Sobrien if (dlen >= (int)strlen(pna)) { 42060484Sobrien if (memcmp(data, pna, strlen(pna)) == 0) { 42160484Sobrien alias_pna_out(la, pip, lnk, data, dlen); 42277298Sobrien } 42377298Sobrien } 42477298Sobrien } else { 42560484Sobrien 42677298Sobrien /* 42777298Sobrien * When aliasing a server, check for the 200 reply 42833965Sjdp * Accomodate varying number of blanks between 200 & OK 42933965Sjdp */ 43077298Sobrien 43177298Sobrien if (dlen >= (int)strlen(str200)) { 43277298Sobrien 43333965Sjdp for (parseOk = 0, i = 0; 43477298Sobrien i <= dlen - (int)strlen(str200); 43577298Sobrien i++) { 43677298Sobrien if (memcmp(&data[i], str200, strlen(str200)) == 0) { 43733965Sjdp parseOk = 1; 43833965Sjdp break; 43933965Sjdp } 44077298Sobrien } 44177298Sobrien if (parseOk) { 44233965Sjdp 44333965Sjdp i += strlen(str200); /* skip string found */ 44433965Sjdp while (data[i] == ' ') /* skip blank(s) */ 44533965Sjdp i++; 44633965Sjdp 44733965Sjdp if ((dlen - i) >= (int)strlen(okstr)) { 44833965Sjdp 44933965Sjdp if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 45033965Sjdp alias_rtsp_out(la, pip, lnk, data, server_port_str); 45133965Sjdp 45233965Sjdp } 45333965Sjdp } 45433965Sjdp } 45533965Sjdp } 45633965Sjdp} 45733965Sjdp