alias_smedia.c revision 99207
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 99207 2002-07-01 11:19:40Z brian $");
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{
12163899Sarchie    int i, j, k;
12263899Sarchie    int search_str_len;
12363899Sarchie
12463899Sarchie    search_str_len = strlen(search_str);
12563899Sarchie    for (i = 0; i < dlen - search_str_len; i++) {
12663899Sarchie	for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
12763899Sarchie	    if (data[j] != search_str[k] &&
12863899Sarchie		data[j] != search_str[k] - ('a' - 'A')) {
12963899Sarchie		break;
13063899Sarchie	    }
13163899Sarchie	    if (k == search_str_len - 1) {
13263899Sarchie		return j + 1;
13363899Sarchie	    }
13463899Sarchie	}
13563899Sarchie    }
13663899Sarchie    return -1;
13763899Sarchie}
13863899Sarchie
13965892Srustatic int
14065892Srualias_rtsp_out(struct ip *pip,
14163899Sarchie		   struct alias_link *link,
14263899Sarchie		   char *data,
14371796Sbrian		   const char *port_str)
14463899Sarchie{
14563899Sarchie    int     hlen, tlen, dlen;
14663899Sarchie    struct tcphdr *tc;
14763899Sarchie    int     i, j, pos, state, port_dlen, new_dlen, delta;
14863899Sarchie    u_short p[2], new_len;
14963899Sarchie    u_short sport, eport, base_port;
15063899Sarchie    u_short salias = 0, ealias = 0, base_alias = 0;
15171796Sbrian    const char *transport_str = "transport:";
15263899Sarchie    char    newdata[2048], *port_data, *port_newdata, stemp[80];
15363899Sarchie    int     links_created = 0, pkt_updated = 0;
15463899Sarchie    struct alias_link *rtsp_link = NULL;
15599207Sbrian    struct in_addr null_addr;
15663899Sarchie
15763899Sarchie    /* Calculate data length of TCP packet */
15863899Sarchie    tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
15963899Sarchie    hlen = (pip->ip_hl + tc->th_off) << 2;
16063899Sarchie    tlen = ntohs(pip->ip_len);
16163899Sarchie    dlen = tlen - hlen;
16263899Sarchie
16363899Sarchie    /* Find keyword, "Transport: " */
16463899Sarchie    pos = search_string(data, dlen, transport_str);
16563899Sarchie    if (pos < 0) {
16663899Sarchie	return -1;
16763899Sarchie    }
16863899Sarchie    port_data = data + pos;
16963899Sarchie    port_dlen = dlen - pos;
17063899Sarchie
17163899Sarchie    memcpy(newdata, data, pos);
17263899Sarchie    port_newdata = newdata + pos;
17363899Sarchie
17463899Sarchie    while (port_dlen > strlen(port_str)) {
17563899Sarchie	/* Find keyword, appropriate port string */
17663899Sarchie	pos = search_string(port_data, port_dlen, port_str);
17763899Sarchie	if (pos < 0) {
17863899Sarchie	    break;
17963899Sarchie	}
18063899Sarchie
18163899Sarchie	memcpy (port_newdata, port_data, pos + 1);
18263899Sarchie	port_newdata += (pos + 1);
18363899Sarchie
18463899Sarchie	p[0] = p[1] = 0;
18563899Sarchie	sport = eport = 0;
18663899Sarchie	state = 0;
18763899Sarchie	for (i = pos; i < port_dlen; i++) {
18863899Sarchie	    switch(state) {
18963899Sarchie	    case 0:
19063899Sarchie		if (port_data[i] == '=') {
19163899Sarchie		    state++;
19263899Sarchie		}
19363899Sarchie		break;
19463899Sarchie	    case 1:
19563899Sarchie		if (ISDIGIT(port_data[i])) {
19663899Sarchie		    p[0] = p[0] * 10 + port_data[i] - '0';
19763899Sarchie		} else {
19863899Sarchie		    if (port_data[i] == ';') {
19963899Sarchie			state = 3;
20063899Sarchie		    }
20163899Sarchie		    if (port_data[i] == '-') {
20263899Sarchie			state++;
20363899Sarchie		    }
20463899Sarchie		}
20563899Sarchie		break;
20663899Sarchie	    case 2:
20763899Sarchie		if (ISDIGIT(port_data[i])) {
20863899Sarchie		    p[1] = p[1] * 10 + port_data[i] - '0';
20963899Sarchie		} else {
21063899Sarchie		    state++;
21163899Sarchie		}
21263899Sarchie		break;
21363899Sarchie	    case 3:
21463899Sarchie		base_port = p[0];
21563899Sarchie		sport = htons(p[0]);
21663899Sarchie		eport = htons(p[1]);
21763899Sarchie
21863899Sarchie		if (!links_created) {
21963899Sarchie
22099207Sbrian	  	  links_created = 1;
22163899Sarchie		  /* Find an even numbered port number base that
22263899Sarchie		     satisfies the contiguous number of ports we need  */
22363899Sarchie		  null_addr.s_addr = 0;
22463899Sarchie		  if (0 == (salias = FindNewPortGroup(null_addr,
22563899Sarchie	       			    FindAliasAddress(pip->ip_src),
22699207Sbrian				    sport, 0,
22799207Sbrian				    RTSP_PORT_GROUP,
22899207Sbrian				    IPPROTO_UDP, 1))) {
22963899Sarchie#ifdef DEBUG
23063899Sarchie		    fprintf(stderr,
23163899Sarchie		    "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
23263899Sarchie#endif
23363899Sarchie		  } else {
23463899Sarchie
23563899Sarchie  		    base_alias = ntohs(salias);
23663899Sarchie		    for (j = 0; j < RTSP_PORT_GROUP; j++) {
23763899Sarchie		      /* Establish link to port found in RTSP packet */
23863899Sarchie		      rtsp_link = FindRtspOut(GetOriginalAddress(link), null_addr,
23963899Sarchie                                htons(base_port + j), htons(base_alias + j),
24063899Sarchie                                IPPROTO_UDP);
24163899Sarchie		      if (rtsp_link != NULL) {
24263899Sarchie#ifndef NO_FW_PUNCH
24363899Sarchie		        /* Punch hole in firewall */
24463899Sarchie		        PunchFWHole(rtsp_link);
24563899Sarchie#endif
24663899Sarchie		      } else {
24763899Sarchie#ifdef DEBUG
24863899Sarchie		        fprintf(stderr,
24963899Sarchie		        "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
25063899Sarchie#endif
25163899Sarchie		        break;
25263899Sarchie		      }
25363899Sarchie		    }
25463899Sarchie		  }
25563899Sarchie                  ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
25663899Sarchie		}
25763899Sarchie
25863899Sarchie		if (salias && rtsp_link) {
25963899Sarchie
26063899Sarchie		  pkt_updated = 1;
26163899Sarchie
26263899Sarchie	          /* Copy into IP packet */
26363899Sarchie		  sprintf(stemp, "%d", ntohs(salias));
26463899Sarchie		  memcpy(port_newdata, stemp, strlen(stemp));
26563899Sarchie		  port_newdata += strlen(stemp);
26663899Sarchie
26763899Sarchie		  if (eport != 0) {
26863899Sarchie		    *port_newdata = '-';
26963899Sarchie		    port_newdata++;
27063899Sarchie
27163899Sarchie		    /* Copy into IP packet */
27263899Sarchie		    sprintf(stemp, "%d", ntohs(ealias));
27363899Sarchie		    memcpy(port_newdata, stemp, strlen(stemp));
27463899Sarchie		    port_newdata += strlen(stemp);
27563899Sarchie		  }
27663899Sarchie
27763899Sarchie	          *port_newdata = ';';
27863899Sarchie		  port_newdata++;
27963899Sarchie		}
28063899Sarchie		state++;
28163899Sarchie		break;
28263899Sarchie	    }
28363899Sarchie	    if (state > 3) {
28463899Sarchie		break;
28563899Sarchie	    }
28663899Sarchie	}
28763899Sarchie	port_data += i;
28863899Sarchie	port_dlen -= i;
28963899Sarchie    }
29063899Sarchie
29163899Sarchie    if (!pkt_updated)
29263899Sarchie      return -1;
29363899Sarchie
29463899Sarchie    memcpy (port_newdata, port_data, port_dlen);
29563899Sarchie    port_newdata += port_dlen;
29663899Sarchie    *port_newdata = '\0';
29763899Sarchie
29863899Sarchie    /* Create new packet */
29963899Sarchie    new_dlen = port_newdata - newdata;
30063899Sarchie    memcpy (data, newdata, new_dlen);
30163899Sarchie
30263899Sarchie    SetAckModified(link);
30363899Sarchie    delta = GetDeltaSeqOut(pip, link);
30463899Sarchie    AddSeq(pip, link, delta + new_dlen - dlen);
30563899Sarchie
30663899Sarchie    new_len = htons(hlen + new_dlen);
30763899Sarchie    DifferentialChecksum(&pip->ip_sum,
30863899Sarchie			 &new_len,
30963899Sarchie			 &pip->ip_len,
31063899Sarchie			 1);
31163899Sarchie    pip->ip_len = new_len;
31263899Sarchie
31363899Sarchie    tc->th_sum = 0;
31463899Sarchie    tc->th_sum = TcpChecksum(pip);
31563899Sarchie
31663899Sarchie    return 0;
31763899Sarchie}
31863899Sarchie
31963899Sarchie/* Support the protocol used by early versions of RealPlayer */
32063899Sarchie
32165892Srustatic int
32265892Srualias_pna_out(struct ip *pip,
32363899Sarchie		  struct alias_link *link,
32463899Sarchie		  char *data,
32563899Sarchie		  int dlen)
32663899Sarchie{
32763899Sarchie    struct alias_link *pna_links;
32863899Sarchie    u_short msg_id, msg_len;
32963899Sarchie    char    *work;
33063899Sarchie    u_short alias_port, port;
33163899Sarchie    struct  tcphdr *tc;
33263899Sarchie
33363899Sarchie    work = data;
33463899Sarchie    work += 5;
33563899Sarchie    while (work + 4 < data + dlen) {
33663899Sarchie	memcpy(&msg_id, work, 2);
33763899Sarchie	work += 2;
33863899Sarchie	memcpy(&msg_len, work, 2);
33963899Sarchie	work += 2;
34063899Sarchie	if (ntohs(msg_id) == 0) {
34163899Sarchie	    /* end of options */
34263899Sarchie	    return 0;
34363899Sarchie	}
34463899Sarchie	if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
34571796Sbrian	    memcpy(&port, work, 2);
34663899Sarchie	    pna_links = FindUdpTcpOut(pip->ip_src, GetDestAddress(link),
34767980Sru				      port, 0, IPPROTO_UDP, 1);
34863899Sarchie	    if (pna_links != NULL) {
34963899Sarchie#ifndef NO_FW_PUNCH
35063899Sarchie		/* Punch hole in firewall */
35163899Sarchie		PunchFWHole(pna_links);
35263899Sarchie#endif
35363899Sarchie		tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
35463899Sarchie		alias_port = GetAliasPort(pna_links);
35571796Sbrian		memcpy(work, &alias_port, 2);
35663899Sarchie
35763899Sarchie		/* Compute TCP checksum for revised packet */
35863899Sarchie		tc->th_sum = 0;
35963899Sarchie		tc->th_sum = TcpChecksum(pip);
36063899Sarchie	    }
36163899Sarchie	}
36263899Sarchie	work += ntohs(msg_len);
36363899Sarchie    }
36499207Sbrian
36563899Sarchie    return 0;
36663899Sarchie}
36763899Sarchie
36863899Sarchievoid
36963899SarchieAliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize)
37063899Sarchie{
37163899Sarchie    int    hlen, tlen, dlen;
37263899Sarchie    struct tcphdr *tc;
37371796Sbrian    char   *data;
37471796Sbrian    const  char *setup = "SETUP", *pna = "PNA", *str200 = "200";
37571796Sbrian    const  char *okstr = "OK", *client_port_str = "client_port";
37671796Sbrian    const  char *server_port_str = "server_port";
37763899Sarchie    int    i, parseOk;
37863899Sarchie
37963899Sarchie    tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
38063899Sarchie    hlen = (pip->ip_hl + tc->th_off) << 2;
38163899Sarchie    tlen = ntohs(pip->ip_len);
38263899Sarchie    dlen = tlen - hlen;
38363899Sarchie
38463899Sarchie    data = (char*)pip;
38563899Sarchie    data += hlen;
38663899Sarchie
38763899Sarchie    /* When aliasing a client, check for the SETUP request */
38899207Sbrian    if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
38999207Sbrian      (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
39063899Sarchie
39163899Sarchie      if (dlen >= strlen(setup)) {
39263899Sarchie        if (memcmp(data, setup, strlen(setup)) == 0) {
39363899Sarchie	    alias_rtsp_out(pip, link, data, client_port_str);
39463899Sarchie	    return;
39563899Sarchie	}
39663899Sarchie      }
39763899Sarchie      if (dlen >= strlen(pna)) {
39863899Sarchie	if (memcmp(data, pna, strlen(pna)) == 0) {
39963899Sarchie	    alias_pna_out(pip, link, data, dlen);
40063899Sarchie	}
40163899Sarchie      }
40263899Sarchie
40363899Sarchie    } else {
40463899Sarchie
40563899Sarchie      /* When aliasing a server, check for the 200 reply
40663899Sarchie         Accomodate varying number of blanks between 200 & OK */
40763899Sarchie
40863899Sarchie      if (dlen >= strlen(str200)) {
40963899Sarchie
41099207Sbrian        for (parseOk = 0, i = 0;
41199207Sbrian             i <= dlen - strlen(str200);
41263899Sarchie             i++) {
41399207Sbrian          if (memcmp(&data[i], str200, strlen(str200)) == 0) {
41499207Sbrian            parseOk = 1;
41563899Sarchie            break;
41663899Sarchie          }
41763899Sarchie        }
41899207Sbrian        if (parseOk) {
41963899Sarchie
42099207Sbrian          i += strlen(str200);        /* skip string found */
42163899Sarchie          while(data[i] == ' ')       /* skip blank(s) */
42263899Sarchie	    i++;
42399207Sbrian
42463899Sarchie          if ((dlen - i) >= strlen(okstr)) {
42563899Sarchie
42699207Sbrian            if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
42763899Sarchie              alias_rtsp_out(pip, link, data, server_port_str);
42863899Sarchie
42963899Sarchie          }
43063899Sarchie        }
43163899Sarchie      }
43263899Sarchie    }
43363899Sarchie}
434