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