alias_smedia.c revision 147623
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 147623 2005-06-27 07:36:02Z glebius $");
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
101145921Sglebius#ifdef _KERNEL
102145921Sglebius#include <sys/param.h>
103145921Sglebius#include <sys/libkern.h>
104145921Sglebius#else
105145921Sglebius#include <sys/types.h>
10663899Sarchie#include <stdio.h>
10763899Sarchie#include <string.h>
108145921Sglebius#endif
109145921Sglebius
11063899Sarchie#include <netinet/in_systm.h>
11163899Sarchie#include <netinet/in.h>
11263899Sarchie#include <netinet/ip.h>
11363899Sarchie#include <netinet/tcp.h>
11463899Sarchie#include <netinet/udp.h>
11563899Sarchie
116145921Sglebius#ifdef _KERNEL
117145932Sglebius#include <netinet/libalias/alias.h>
118145921Sglebius#include <netinet/libalias/alias_local.h>
119145921Sglebius#else
12063899Sarchie#include "alias_local.h"
121145921Sglebius#endif
12263899Sarchie
12399207Sbrian#define RTSP_CONTROL_PORT_NUMBER_1 554
12499207Sbrian#define RTSP_CONTROL_PORT_NUMBER_2 7070
12563899Sarchie#define RTSP_PORT_GROUP            2
12663899Sarchie
12763899Sarchie#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
12863899Sarchie
12965892Srustatic int
13071796Sbriansearch_string(char *data, int dlen, const char *search_str)
13163899Sarchie{
132127094Sdes	int i, j, k;
133127094Sdes	int search_str_len;
13463899Sarchie
135127094Sdes	search_str_len = strlen(search_str);
136127094Sdes	for (i = 0; i < dlen - search_str_len; i++) {
137127094Sdes		for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
138127094Sdes			if (data[j] != search_str[k] &&
139127094Sdes			    data[j] != search_str[k] - ('a' - 'A')) {
140127094Sdes				break;
141127094Sdes			}
142127094Sdes			if (k == search_str_len - 1) {
143131613Sdes				return (j + 1);
144127094Sdes			}
145127094Sdes		}
14663899Sarchie	}
147131613Sdes	return (-1);
14863899Sarchie}
14963899Sarchie
15065892Srustatic int
151124621Sphkalias_rtsp_out(struct libalias *la, struct ip *pip,
152131614Sdes    struct alias_link *lnk,
153127094Sdes    char *data,
154127094Sdes    const char *port_str)
15563899Sarchie{
156127094Sdes	int hlen, tlen, dlen;
157127094Sdes	struct tcphdr *tc;
158127094Sdes	int i, j, pos, state, port_dlen, new_dlen, delta;
159127094Sdes	u_short p[2], new_len;
160127094Sdes	u_short sport, eport, base_port;
161127094Sdes	u_short salias = 0, ealias = 0, base_alias = 0;
162127094Sdes	const char *transport_str = "transport:";
163127094Sdes	char newdata[2048], *port_data, *port_newdata, stemp[80];
164127094Sdes	int links_created = 0, pkt_updated = 0;
165131614Sdes	struct alias_link *rtsp_lnk = NULL;
166127094Sdes	struct in_addr null_addr;
16763899Sarchie
168127094Sdes	/* Calculate data length of TCP packet */
169131699Sdes	tc = (struct tcphdr *)ip_next(pip);
170127094Sdes	hlen = (pip->ip_hl + tc->th_off) << 2;
171127094Sdes	tlen = ntohs(pip->ip_len);
172127094Sdes	dlen = tlen - hlen;
17363899Sarchie
174127094Sdes	/* Find keyword, "Transport: " */
175127094Sdes	pos = search_string(data, dlen, transport_str);
17663899Sarchie	if (pos < 0) {
177131613Sdes		return (-1);
17863899Sarchie	}
179127094Sdes	port_data = data + pos;
180127094Sdes	port_dlen = dlen - pos;
18163899Sarchie
182127094Sdes	memcpy(newdata, data, pos);
183127094Sdes	port_newdata = newdata + pos;
18463899Sarchie
185131614Sdes	while (port_dlen > (int)strlen(port_str)) {
186127094Sdes		/* Find keyword, appropriate port string */
187127094Sdes		pos = search_string(port_data, port_dlen, port_str);
188127094Sdes		if (pos < 0) {
189127094Sdes			break;
19063899Sarchie		}
191127094Sdes		memcpy(port_newdata, port_data, pos + 1);
192127094Sdes		port_newdata += (pos + 1);
19363899Sarchie
194127094Sdes		p[0] = p[1] = 0;
195127094Sdes		sport = eport = 0;
196127094Sdes		state = 0;
197127094Sdes		for (i = pos; i < port_dlen; i++) {
198127094Sdes			switch (state) {
199127094Sdes			case 0:
200127094Sdes				if (port_data[i] == '=') {
201127094Sdes					state++;
202127094Sdes				}
203127094Sdes				break;
204127094Sdes			case 1:
205127094Sdes				if (ISDIGIT(port_data[i])) {
206127094Sdes					p[0] = p[0] * 10 + port_data[i] - '0';
207127094Sdes				} else {
208127094Sdes					if (port_data[i] == ';') {
209127094Sdes						state = 3;
210127094Sdes					}
211127094Sdes					if (port_data[i] == '-') {
212127094Sdes						state++;
213127094Sdes					}
214127094Sdes				}
215127094Sdes				break;
216127094Sdes			case 2:
217127094Sdes				if (ISDIGIT(port_data[i])) {
218127094Sdes					p[1] = p[1] * 10 + port_data[i] - '0';
219127094Sdes				} else {
220127094Sdes					state++;
221127094Sdes				}
222127094Sdes				break;
223127094Sdes			case 3:
224127094Sdes				base_port = p[0];
225127094Sdes				sport = htons(p[0]);
226127094Sdes				eport = htons(p[1]);
22763899Sarchie
228127094Sdes				if (!links_created) {
229127094Sdes
230127094Sdes					links_created = 1;
231127094Sdes					/*
232127094Sdes					 * Find an even numbered port
233127094Sdes					 * number base that satisfies the
234127094Sdes					 * contiguous number of ports we
235127094Sdes					 * need
236127094Sdes					 */
237127094Sdes					null_addr.s_addr = 0;
238127094Sdes					if (0 == (salias = FindNewPortGroup(la, null_addr,
239127094Sdes					    FindAliasAddress(la, pip->ip_src),
240127094Sdes					    sport, 0,
241127094Sdes					    RTSP_PORT_GROUP,
242127094Sdes					    IPPROTO_UDP, 1))) {
243145961Sglebius#ifdef LIBALIAS_DEBUG
244127094Sdes						fprintf(stderr,
245127094Sdes						    "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
24663899Sarchie#endif
247127094Sdes					} else {
24863899Sarchie
249127094Sdes						base_alias = ntohs(salias);
250127094Sdes						for (j = 0; j < RTSP_PORT_GROUP; j++) {
251127094Sdes							/*
252127094Sdes							 * Establish link
253127094Sdes							 * to port found in
254127094Sdes							 * RTSP packet
255127094Sdes							 */
256131614Sdes							rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
257127094Sdes							    htons(base_port + j), htons(base_alias + j),
258127094Sdes							    IPPROTO_UDP);
259131614Sdes							if (rtsp_lnk != NULL) {
26063899Sarchie#ifndef NO_FW_PUNCH
261127094Sdes								/*
262127094Sdes								 * Punch
263127094Sdes								 * hole in
264127094Sdes								 * firewall
265127094Sdes								 */
266131614Sdes								PunchFWHole(rtsp_lnk);
26763899Sarchie#endif
268127094Sdes							} else {
269145961Sglebius#ifdef LIBALIAS_DEBUG
270127094Sdes								fprintf(stderr,
271127094Sdes								    "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
27263899Sarchie#endif
273127094Sdes								break;
274127094Sdes							}
275127094Sdes						}
276127094Sdes					}
277127094Sdes					ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
278127094Sdes				}
279131614Sdes				if (salias && rtsp_lnk) {
28063899Sarchie
281127094Sdes					pkt_updated = 1;
28263899Sarchie
283127094Sdes					/* Copy into IP packet */
284127094Sdes					sprintf(stemp, "%d", ntohs(salias));
285127094Sdes					memcpy(port_newdata, stemp, strlen(stemp));
286127094Sdes					port_newdata += strlen(stemp);
28763899Sarchie
288127094Sdes					if (eport != 0) {
289127094Sdes						*port_newdata = '-';
290127094Sdes						port_newdata++;
29163899Sarchie
292127094Sdes						/* Copy into IP packet */
293127094Sdes						sprintf(stemp, "%d", ntohs(ealias));
294127094Sdes						memcpy(port_newdata, stemp, strlen(stemp));
295127094Sdes						port_newdata += strlen(stemp);
296127094Sdes					}
297127094Sdes					*port_newdata = ';';
298127094Sdes					port_newdata++;
299127094Sdes				}
300127094Sdes				state++;
301127094Sdes				break;
302127094Sdes			}
303127094Sdes			if (state > 3) {
304127094Sdes				break;
305127094Sdes			}
30663899Sarchie		}
307127094Sdes		port_data += i;
308127094Sdes		port_dlen -= i;
30963899Sarchie	}
31063899Sarchie
311127094Sdes	if (!pkt_updated)
312131613Sdes		return (-1);
31363899Sarchie
314127094Sdes	memcpy(port_newdata, port_data, port_dlen);
315127094Sdes	port_newdata += port_dlen;
316127094Sdes	*port_newdata = '\0';
31763899Sarchie
318127094Sdes	/* Create new packet */
319127094Sdes	new_dlen = port_newdata - newdata;
320127094Sdes	memcpy(data, newdata, new_dlen);
32163899Sarchie
322131614Sdes	SetAckModified(lnk);
323131614Sdes	delta = GetDeltaSeqOut(pip, lnk);
324131614Sdes	AddSeq(pip, lnk, delta + new_dlen - dlen);
32563899Sarchie
326127094Sdes	new_len = htons(hlen + new_dlen);
327127094Sdes	DifferentialChecksum(&pip->ip_sum,
328127094Sdes	    &new_len,
329127094Sdes	    &pip->ip_len,
330127094Sdes	    1);
331127094Sdes	pip->ip_len = new_len;
33263899Sarchie
333127094Sdes	tc->th_sum = 0;
334147623Sglebius#ifdef _KERNEL
335147623Sglebius	tc->th_x2 = 1;
336147623Sglebius#else
337127094Sdes	tc->th_sum = TcpChecksum(pip);
338147623Sglebius#endif
339131613Sdes	return (0);
34063899Sarchie}
34163899Sarchie
34263899Sarchie/* Support the protocol used by early versions of RealPlayer */
34363899Sarchie
34465892Srustatic int
345124621Sphkalias_pna_out(struct libalias *la, struct ip *pip,
346131614Sdes    struct alias_link *lnk,
347127094Sdes    char *data,
348127094Sdes    int dlen)
34963899Sarchie{
350127094Sdes	struct alias_link *pna_links;
351127094Sdes	u_short msg_id, msg_len;
352127094Sdes	char *work;
353127094Sdes	u_short alias_port, port;
354127094Sdes	struct tcphdr *tc;
35563899Sarchie
356127094Sdes	work = data;
357127094Sdes	work += 5;
358127094Sdes	while (work + 4 < data + dlen) {
359127094Sdes		memcpy(&msg_id, work, 2);
360127094Sdes		work += 2;
361127094Sdes		memcpy(&msg_len, work, 2);
362127094Sdes		work += 2;
363127094Sdes		if (ntohs(msg_id) == 0) {
364127094Sdes			/* end of options */
365131613Sdes			return (0);
366127094Sdes		}
367127094Sdes		if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
368127094Sdes			memcpy(&port, work, 2);
369131614Sdes			pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
370127094Sdes			    port, 0, IPPROTO_UDP, 1);
371127094Sdes			if (pna_links != NULL) {
37263899Sarchie#ifndef NO_FW_PUNCH
373127094Sdes				/* Punch hole in firewall */
374127094Sdes				PunchFWHole(pna_links);
37563899Sarchie#endif
376131699Sdes				tc = (struct tcphdr *)ip_next(pip);
377127094Sdes				alias_port = GetAliasPort(pna_links);
378127094Sdes				memcpy(work, &alias_port, 2);
37963899Sarchie
380127094Sdes				/* Compute TCP checksum for revised packet */
381127094Sdes				tc->th_sum = 0;
382147623Sglebius#ifdef _KERNEL
383147623Sglebius				tc->th_x2 = 1;
384147623Sglebius#else
385127094Sdes				tc->th_sum = TcpChecksum(pip);
386147623Sglebius#endif
387127094Sdes			}
388127094Sdes		}
389127094Sdes		work += ntohs(msg_len);
39063899Sarchie	}
39199207Sbrian
392131613Sdes	return (0);
39363899Sarchie}
39463899Sarchie
39563899Sarchievoid
396131614SdesAliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
39763899Sarchie{
398127094Sdes	int hlen, tlen, dlen;
399127094Sdes	struct tcphdr *tc;
400127094Sdes	char *data;
401127094Sdes	const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
402127094Sdes	const char *okstr = "OK", *client_port_str = "client_port";
403127094Sdes	const char *server_port_str = "server_port";
404127094Sdes	int i, parseOk;
40563899Sarchie
406131614Sdes	(void)maxpacketsize;
407131614Sdes
408131699Sdes	tc = (struct tcphdr *)ip_next(pip);
409127094Sdes	hlen = (pip->ip_hl + tc->th_off) << 2;
410127094Sdes	tlen = ntohs(pip->ip_len);
411127094Sdes	dlen = tlen - hlen;
41263899Sarchie
413127094Sdes	data = (char *)pip;
414127094Sdes	data += hlen;
41563899Sarchie
416127094Sdes	/* When aliasing a client, check for the SETUP request */
417127094Sdes	if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
418127094Sdes	    (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
41963899Sarchie
420131614Sdes		if (dlen >= (int)strlen(setup)) {
421127094Sdes			if (memcmp(data, setup, strlen(setup)) == 0) {
422131614Sdes				alias_rtsp_out(la, pip, lnk, data, client_port_str);
423127094Sdes				return;
424127094Sdes			}
425127094Sdes		}
426131614Sdes		if (dlen >= (int)strlen(pna)) {
427127094Sdes			if (memcmp(data, pna, strlen(pna)) == 0) {
428131614Sdes				alias_pna_out(la, pip, lnk, data, dlen);
429127094Sdes			}
430127094Sdes		}
431127094Sdes	} else {
43263899Sarchie
433127094Sdes		/*
434127094Sdes		 * When aliasing a server, check for the 200 reply
435127094Sdes		 * Accomodate varying number of blanks between 200 & OK
436127094Sdes		 */
43763899Sarchie
438131614Sdes		if (dlen >= (int)strlen(str200)) {
43963899Sarchie
440127094Sdes			for (parseOk = 0, i = 0;
441131614Sdes			    i <= dlen - (int)strlen(str200);
442127094Sdes			    i++) {
443127094Sdes				if (memcmp(&data[i], str200, strlen(str200)) == 0) {
444127094Sdes					parseOk = 1;
445127094Sdes					break;
446127094Sdes				}
447127094Sdes			}
448127094Sdes			if (parseOk) {
44963899Sarchie
450127094Sdes				i += strlen(str200);	/* skip string found */
451127094Sdes				while (data[i] == ' ')	/* skip blank(s) */
452127094Sdes					i++;
45363899Sarchie
454131614Sdes				if ((dlen - i) >= (int)strlen(okstr)) {
45599207Sbrian
456127094Sdes					if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
457131614Sdes						alias_rtsp_out(la, pip, lnk, data, server_port_str);
45863899Sarchie
459127094Sdes				}
460127094Sdes			}
461127094Sdes		}
462127094Sdes	}
46363899Sarchie}
464