alias_smedia.c revision 131699
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 131699 2004-07-06 12:13:28Z des $");
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{
121127094Sdes	int i, j, k;
122127094Sdes	int search_str_len;
12363899Sarchie
124127094Sdes	search_str_len = strlen(search_str);
125127094Sdes	for (i = 0; i < dlen - search_str_len; i++) {
126127094Sdes		for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
127127094Sdes			if (data[j] != search_str[k] &&
128127094Sdes			    data[j] != search_str[k] - ('a' - 'A')) {
129127094Sdes				break;
130127094Sdes			}
131127094Sdes			if (k == search_str_len - 1) {
132131613Sdes				return (j + 1);
133127094Sdes			}
134127094Sdes		}
13563899Sarchie	}
136131613Sdes	return (-1);
13763899Sarchie}
13863899Sarchie
13965892Srustatic int
140124621Sphkalias_rtsp_out(struct libalias *la, struct ip *pip,
141131614Sdes    struct alias_link *lnk,
142127094Sdes    char *data,
143127094Sdes    const char *port_str)
14463899Sarchie{
145127094Sdes	int hlen, tlen, dlen;
146127094Sdes	struct tcphdr *tc;
147127094Sdes	int i, j, pos, state, port_dlen, new_dlen, delta;
148127094Sdes	u_short p[2], new_len;
149127094Sdes	u_short sport, eport, base_port;
150127094Sdes	u_short salias = 0, ealias = 0, base_alias = 0;
151127094Sdes	const char *transport_str = "transport:";
152127094Sdes	char newdata[2048], *port_data, *port_newdata, stemp[80];
153127094Sdes	int links_created = 0, pkt_updated = 0;
154131614Sdes	struct alias_link *rtsp_lnk = NULL;
155127094Sdes	struct in_addr null_addr;
15663899Sarchie
157127094Sdes	/* Calculate data length of TCP packet */
158131699Sdes	tc = (struct tcphdr *)ip_next(pip);
159127094Sdes	hlen = (pip->ip_hl + tc->th_off) << 2;
160127094Sdes	tlen = ntohs(pip->ip_len);
161127094Sdes	dlen = tlen - hlen;
16263899Sarchie
163127094Sdes	/* Find keyword, "Transport: " */
164127094Sdes	pos = search_string(data, dlen, transport_str);
16563899Sarchie	if (pos < 0) {
166131613Sdes		return (-1);
16763899Sarchie	}
168127094Sdes	port_data = data + pos;
169127094Sdes	port_dlen = dlen - pos;
17063899Sarchie
171127094Sdes	memcpy(newdata, data, pos);
172127094Sdes	port_newdata = newdata + pos;
17363899Sarchie
174131614Sdes	while (port_dlen > (int)strlen(port_str)) {
175127094Sdes		/* Find keyword, appropriate port string */
176127094Sdes		pos = search_string(port_data, port_dlen, port_str);
177127094Sdes		if (pos < 0) {
178127094Sdes			break;
17963899Sarchie		}
180127094Sdes		memcpy(port_newdata, port_data, pos + 1);
181127094Sdes		port_newdata += (pos + 1);
18263899Sarchie
183127094Sdes		p[0] = p[1] = 0;
184127094Sdes		sport = eport = 0;
185127094Sdes		state = 0;
186127094Sdes		for (i = pos; i < port_dlen; i++) {
187127094Sdes			switch (state) {
188127094Sdes			case 0:
189127094Sdes				if (port_data[i] == '=') {
190127094Sdes					state++;
191127094Sdes				}
192127094Sdes				break;
193127094Sdes			case 1:
194127094Sdes				if (ISDIGIT(port_data[i])) {
195127094Sdes					p[0] = p[0] * 10 + port_data[i] - '0';
196127094Sdes				} else {
197127094Sdes					if (port_data[i] == ';') {
198127094Sdes						state = 3;
199127094Sdes					}
200127094Sdes					if (port_data[i] == '-') {
201127094Sdes						state++;
202127094Sdes					}
203127094Sdes				}
204127094Sdes				break;
205127094Sdes			case 2:
206127094Sdes				if (ISDIGIT(port_data[i])) {
207127094Sdes					p[1] = p[1] * 10 + port_data[i] - '0';
208127094Sdes				} else {
209127094Sdes					state++;
210127094Sdes				}
211127094Sdes				break;
212127094Sdes			case 3:
213127094Sdes				base_port = p[0];
214127094Sdes				sport = htons(p[0]);
215127094Sdes				eport = htons(p[1]);
21663899Sarchie
217127094Sdes				if (!links_created) {
218127094Sdes
219127094Sdes					links_created = 1;
220127094Sdes					/*
221127094Sdes					 * Find an even numbered port
222127094Sdes					 * number base that satisfies the
223127094Sdes					 * contiguous number of ports we
224127094Sdes					 * need
225127094Sdes					 */
226127094Sdes					null_addr.s_addr = 0;
227127094Sdes					if (0 == (salias = FindNewPortGroup(la, null_addr,
228127094Sdes					    FindAliasAddress(la, pip->ip_src),
229127094Sdes					    sport, 0,
230127094Sdes					    RTSP_PORT_GROUP,
231127094Sdes					    IPPROTO_UDP, 1))) {
23263899Sarchie#ifdef DEBUG
233127094Sdes						fprintf(stderr,
234127094Sdes						    "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
23563899Sarchie#endif
236127094Sdes					} else {
23763899Sarchie
238127094Sdes						base_alias = ntohs(salias);
239127094Sdes						for (j = 0; j < RTSP_PORT_GROUP; j++) {
240127094Sdes							/*
241127094Sdes							 * Establish link
242127094Sdes							 * to port found in
243127094Sdes							 * RTSP packet
244127094Sdes							 */
245131614Sdes							rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
246127094Sdes							    htons(base_port + j), htons(base_alias + j),
247127094Sdes							    IPPROTO_UDP);
248131614Sdes							if (rtsp_lnk != NULL) {
24963899Sarchie#ifndef NO_FW_PUNCH
250127094Sdes								/*
251127094Sdes								 * Punch
252127094Sdes								 * hole in
253127094Sdes								 * firewall
254127094Sdes								 */
255131614Sdes								PunchFWHole(rtsp_lnk);
25663899Sarchie#endif
257127094Sdes							} else {
25863899Sarchie#ifdef DEBUG
259127094Sdes								fprintf(stderr,
260127094Sdes								    "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
26163899Sarchie#endif
262127094Sdes								break;
263127094Sdes							}
264127094Sdes						}
265127094Sdes					}
266127094Sdes					ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
267127094Sdes				}
268131614Sdes				if (salias && rtsp_lnk) {
26963899Sarchie
270127094Sdes					pkt_updated = 1;
27163899Sarchie
272127094Sdes					/* Copy into IP packet */
273127094Sdes					sprintf(stemp, "%d", ntohs(salias));
274127094Sdes					memcpy(port_newdata, stemp, strlen(stemp));
275127094Sdes					port_newdata += strlen(stemp);
27663899Sarchie
277127094Sdes					if (eport != 0) {
278127094Sdes						*port_newdata = '-';
279127094Sdes						port_newdata++;
28063899Sarchie
281127094Sdes						/* Copy into IP packet */
282127094Sdes						sprintf(stemp, "%d", ntohs(ealias));
283127094Sdes						memcpy(port_newdata, stemp, strlen(stemp));
284127094Sdes						port_newdata += strlen(stemp);
285127094Sdes					}
286127094Sdes					*port_newdata = ';';
287127094Sdes					port_newdata++;
288127094Sdes				}
289127094Sdes				state++;
290127094Sdes				break;
291127094Sdes			}
292127094Sdes			if (state > 3) {
293127094Sdes				break;
294127094Sdes			}
29563899Sarchie		}
296127094Sdes		port_data += i;
297127094Sdes		port_dlen -= i;
29863899Sarchie	}
29963899Sarchie
300127094Sdes	if (!pkt_updated)
301131613Sdes		return (-1);
30263899Sarchie
303127094Sdes	memcpy(port_newdata, port_data, port_dlen);
304127094Sdes	port_newdata += port_dlen;
305127094Sdes	*port_newdata = '\0';
30663899Sarchie
307127094Sdes	/* Create new packet */
308127094Sdes	new_dlen = port_newdata - newdata;
309127094Sdes	memcpy(data, newdata, new_dlen);
31063899Sarchie
311131614Sdes	SetAckModified(lnk);
312131614Sdes	delta = GetDeltaSeqOut(pip, lnk);
313131614Sdes	AddSeq(pip, lnk, delta + new_dlen - dlen);
31463899Sarchie
315127094Sdes	new_len = htons(hlen + new_dlen);
316127094Sdes	DifferentialChecksum(&pip->ip_sum,
317127094Sdes	    &new_len,
318127094Sdes	    &pip->ip_len,
319127094Sdes	    1);
320127094Sdes	pip->ip_len = new_len;
32163899Sarchie
322127094Sdes	tc->th_sum = 0;
323127094Sdes	tc->th_sum = TcpChecksum(pip);
32463899Sarchie
325131613Sdes	return (0);
32663899Sarchie}
32763899Sarchie
32863899Sarchie/* Support the protocol used by early versions of RealPlayer */
32963899Sarchie
33065892Srustatic int
331124621Sphkalias_pna_out(struct libalias *la, struct ip *pip,
332131614Sdes    struct alias_link *lnk,
333127094Sdes    char *data,
334127094Sdes    int dlen)
33563899Sarchie{
336127094Sdes	struct alias_link *pna_links;
337127094Sdes	u_short msg_id, msg_len;
338127094Sdes	char *work;
339127094Sdes	u_short alias_port, port;
340127094Sdes	struct tcphdr *tc;
34163899Sarchie
342127094Sdes	work = data;
343127094Sdes	work += 5;
344127094Sdes	while (work + 4 < data + dlen) {
345127094Sdes		memcpy(&msg_id, work, 2);
346127094Sdes		work += 2;
347127094Sdes		memcpy(&msg_len, work, 2);
348127094Sdes		work += 2;
349127094Sdes		if (ntohs(msg_id) == 0) {
350127094Sdes			/* end of options */
351131613Sdes			return (0);
352127094Sdes		}
353127094Sdes		if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
354127094Sdes			memcpy(&port, work, 2);
355131614Sdes			pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
356127094Sdes			    port, 0, IPPROTO_UDP, 1);
357127094Sdes			if (pna_links != NULL) {
35863899Sarchie#ifndef NO_FW_PUNCH
359127094Sdes				/* Punch hole in firewall */
360127094Sdes				PunchFWHole(pna_links);
36163899Sarchie#endif
362131699Sdes				tc = (struct tcphdr *)ip_next(pip);
363127094Sdes				alias_port = GetAliasPort(pna_links);
364127094Sdes				memcpy(work, &alias_port, 2);
36563899Sarchie
366127094Sdes				/* Compute TCP checksum for revised packet */
367127094Sdes				tc->th_sum = 0;
368127094Sdes				tc->th_sum = TcpChecksum(pip);
369127094Sdes			}
370127094Sdes		}
371127094Sdes		work += ntohs(msg_len);
37263899Sarchie	}
37399207Sbrian
374131613Sdes	return (0);
37563899Sarchie}
37663899Sarchie
37763899Sarchievoid
378131614SdesAliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
37963899Sarchie{
380127094Sdes	int hlen, tlen, dlen;
381127094Sdes	struct tcphdr *tc;
382127094Sdes	char *data;
383127094Sdes	const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
384127094Sdes	const char *okstr = "OK", *client_port_str = "client_port";
385127094Sdes	const char *server_port_str = "server_port";
386127094Sdes	int i, parseOk;
38763899Sarchie
388131614Sdes	(void)maxpacketsize;
389131614Sdes
390131699Sdes	tc = (struct tcphdr *)ip_next(pip);
391127094Sdes	hlen = (pip->ip_hl + tc->th_off) << 2;
392127094Sdes	tlen = ntohs(pip->ip_len);
393127094Sdes	dlen = tlen - hlen;
39463899Sarchie
395127094Sdes	data = (char *)pip;
396127094Sdes	data += hlen;
39763899Sarchie
398127094Sdes	/* When aliasing a client, check for the SETUP request */
399127094Sdes	if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
400127094Sdes	    (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
40163899Sarchie
402131614Sdes		if (dlen >= (int)strlen(setup)) {
403127094Sdes			if (memcmp(data, setup, strlen(setup)) == 0) {
404131614Sdes				alias_rtsp_out(la, pip, lnk, data, client_port_str);
405127094Sdes				return;
406127094Sdes			}
407127094Sdes		}
408131614Sdes		if (dlen >= (int)strlen(pna)) {
409127094Sdes			if (memcmp(data, pna, strlen(pna)) == 0) {
410131614Sdes				alias_pna_out(la, pip, lnk, data, dlen);
411127094Sdes			}
412127094Sdes		}
413127094Sdes	} else {
41463899Sarchie
415127094Sdes		/*
416127094Sdes		 * When aliasing a server, check for the 200 reply
417127094Sdes		 * Accomodate varying number of blanks between 200 & OK
418127094Sdes		 */
41963899Sarchie
420131614Sdes		if (dlen >= (int)strlen(str200)) {
42163899Sarchie
422127094Sdes			for (parseOk = 0, i = 0;
423131614Sdes			    i <= dlen - (int)strlen(str200);
424127094Sdes			    i++) {
425127094Sdes				if (memcmp(&data[i], str200, strlen(str200)) == 0) {
426127094Sdes					parseOk = 1;
427127094Sdes					break;
428127094Sdes				}
429127094Sdes			}
430127094Sdes			if (parseOk) {
43163899Sarchie
432127094Sdes				i += strlen(str200);	/* skip string found */
433127094Sdes				while (data[i] == ' ')	/* skip blank(s) */
434127094Sdes					i++;
43563899Sarchie
436131614Sdes				if ((dlen - i) >= (int)strlen(okstr)) {
43799207Sbrian
438127094Sdes					if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
439131614Sdes						alias_rtsp_out(la, pip, lnk, data, server_port_str);
44063899Sarchie
441127094Sdes				}
442127094Sdes			}
443127094Sdes		}
444127094Sdes	}
44563899Sarchie}
446