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