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