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