alias_smedia.c revision 162674
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 162674 2006-09-26 23:26:53Z piso $");
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/systm.h>
104#include <sys/kernel.h>
105#include <sys/module.h>
106#else
107#include <errno.h>
108#include <sys/types.h>
109#include <stdio.h>
110#include <string.h>
111#endif
112
113#include <netinet/in_systm.h>
114#include <netinet/in.h>
115#include <netinet/ip.h>
116#include <netinet/tcp.h>
117
118#ifdef _KERNEL
119#include <netinet/libalias/alias.h>
120#include <netinet/libalias/alias_local.h>
121#include <netinet/libalias/alias_mod.h>
122#else
123#include "alias_local.h"
124#include "alias_mod.h"
125#endif
126
127#define RTSP_CONTROL_PORT_NUMBER_1 554
128#define RTSP_CONTROL_PORT_NUMBER_2 7070
129#define TFTP_PORT_NUMBER 69
130
131static void
132AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *,
133		  int maxpacketsize);
134static int
135fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
136{
137
138	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
139	    ah->maxpktsize == 0)
140		return (-1);
141	if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1
142	    || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1
143	    || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2
144	    || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2
145	    || ntohs(*ah->dport) == TFTP_PORT_NUMBER)
146		return (0);
147	return (-1);
148}
149
150static int
151protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
152{
153
154	if (ntohs(*ah->dport) == TFTP_PORT_NUMBER)
155		FindRtspOut(la, pip->ip_src, pip->ip_dst,
156 			    *ah->sport, *ah->aport, IPPROTO_UDP);
157	else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize);
158	return (0);
159}
160
161struct proto_handler handlers[] = {
162	{
163	  .pri = 100,
164	  .dir = OUT,
165	  .proto = TCP|UDP,
166	  .fingerprint = &fingerprint,
167	  .protohandler = &protohandler
168	},
169	{ EOH }
170};
171
172static int
173mod_handler(module_t mod, int type, void *data)
174{
175	int error;
176
177	switch (type) {
178	case MOD_LOAD:
179		error = 0;
180		LibAliasAttachHandlers(handlers);
181		break;
182	case MOD_UNLOAD:
183		error = 0;
184		LibAliasDetachHandlers(handlers);
185		break;
186	default:
187		error = EINVAL;
188	}
189	return (error);
190}
191
192#ifdef _KERNEL
193static
194#endif
195moduledata_t alias_mod = {
196       "alias_smedia", mod_handler, NULL
197};
198
199#ifdef	_KERNEL
200DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
201MODULE_VERSION(alias_smedia, 1);
202MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1);
203#endif
204
205#define RTSP_CONTROL_PORT_NUMBER_1 554
206#define RTSP_CONTROL_PORT_NUMBER_2 7070
207#define RTSP_PORT_GROUP            2
208
209#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
210
211static int
212search_string(char *data, int dlen, const char *search_str)
213{
214	int i, j, k;
215	int search_str_len;
216
217	search_str_len = strlen(search_str);
218	for (i = 0; i < dlen - search_str_len; i++) {
219		for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
220			if (data[j] != search_str[k] &&
221			    data[j] != search_str[k] - ('a' - 'A')) {
222				break;
223			}
224			if (k == search_str_len - 1) {
225				return (j + 1);
226			}
227		}
228	}
229	return (-1);
230}
231
232static int
233alias_rtsp_out(struct libalias *la, struct ip *pip,
234    struct alias_link *lnk,
235    char *data,
236    const char *port_str)
237{
238	int hlen, tlen, dlen;
239	struct tcphdr *tc;
240	int i, j, pos, state, port_dlen, new_dlen, delta;
241	u_short p[2], new_len;
242	u_short sport, eport, base_port;
243	u_short salias = 0, ealias = 0, base_alias = 0;
244	const char *transport_str = "transport:";
245	char newdata[2048], *port_data, *port_newdata, stemp[80];
246	int links_created = 0, pkt_updated = 0;
247	struct alias_link *rtsp_lnk = NULL;
248	struct in_addr null_addr;
249
250	/* Calculate data length of TCP packet */
251	tc = (struct tcphdr *)ip_next(pip);
252	hlen = (pip->ip_hl + tc->th_off) << 2;
253	tlen = ntohs(pip->ip_len);
254	dlen = tlen - hlen;
255
256	/* Find keyword, "Transport: " */
257	pos = search_string(data, dlen, transport_str);
258	if (pos < 0) {
259		return (-1);
260	}
261	port_data = data + pos;
262	port_dlen = dlen - pos;
263
264	memcpy(newdata, data, pos);
265	port_newdata = newdata + pos;
266
267	while (port_dlen > (int)strlen(port_str)) {
268		/* Find keyword, appropriate port string */
269		pos = search_string(port_data, port_dlen, port_str);
270		if (pos < 0) {
271			break;
272		}
273		memcpy(port_newdata, port_data, pos + 1);
274		port_newdata += (pos + 1);
275
276		p[0] = p[1] = 0;
277		sport = eport = 0;
278		state = 0;
279		for (i = pos; i < port_dlen; i++) {
280			switch (state) {
281			case 0:
282				if (port_data[i] == '=') {
283					state++;
284				}
285				break;
286			case 1:
287				if (ISDIGIT(port_data[i])) {
288					p[0] = p[0] * 10 + port_data[i] - '0';
289				} else {
290					if (port_data[i] == ';') {
291						state = 3;
292					}
293					if (port_data[i] == '-') {
294						state++;
295					}
296				}
297				break;
298			case 2:
299				if (ISDIGIT(port_data[i])) {
300					p[1] = p[1] * 10 + port_data[i] - '0';
301				} else {
302					state++;
303				}
304				break;
305			case 3:
306				base_port = p[0];
307				sport = htons(p[0]);
308				eport = htons(p[1]);
309
310				if (!links_created) {
311
312					links_created = 1;
313					/*
314					 * Find an even numbered port
315					 * number base that satisfies the
316					 * contiguous number of ports we
317					 * need
318					 */
319					null_addr.s_addr = 0;
320					if (0 == (salias = FindNewPortGroup(la, null_addr,
321					    FindAliasAddress(la, pip->ip_src),
322					    sport, 0,
323					    RTSP_PORT_GROUP,
324					    IPPROTO_UDP, 1))) {
325#ifdef LIBALIAS_DEBUG
326						fprintf(stderr,
327						    "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
328#endif
329					} else {
330
331						base_alias = ntohs(salias);
332						for (j = 0; j < RTSP_PORT_GROUP; j++) {
333							/*
334							 * Establish link
335							 * to port found in
336							 * RTSP packet
337							 */
338							rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
339							    htons(base_port + j), htons(base_alias + j),
340							    IPPROTO_UDP);
341							if (rtsp_lnk != NULL) {
342#ifndef NO_FW_PUNCH
343								/*
344								 * Punch
345								 * hole in
346								 * firewall
347								 */
348								PunchFWHole(rtsp_lnk);
349#endif
350							} else {
351#ifdef LIBALIAS_DEBUG
352								fprintf(stderr,
353								    "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
354#endif
355								break;
356							}
357						}
358					}
359					ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
360				}
361				if (salias && rtsp_lnk) {
362
363					pkt_updated = 1;
364
365					/* Copy into IP packet */
366					sprintf(stemp, "%d", ntohs(salias));
367					memcpy(port_newdata, stemp, strlen(stemp));
368					port_newdata += strlen(stemp);
369
370					if (eport != 0) {
371						*port_newdata = '-';
372						port_newdata++;
373
374						/* Copy into IP packet */
375						sprintf(stemp, "%d", ntohs(ealias));
376						memcpy(port_newdata, stemp, strlen(stemp));
377						port_newdata += strlen(stemp);
378					}
379					*port_newdata = ';';
380					port_newdata++;
381				}
382				state++;
383				break;
384			}
385			if (state > 3) {
386				break;
387			}
388		}
389		port_data += i;
390		port_dlen -= i;
391	}
392
393	if (!pkt_updated)
394		return (-1);
395
396	memcpy(port_newdata, port_data, port_dlen);
397	port_newdata += port_dlen;
398	*port_newdata = '\0';
399
400	/* Create new packet */
401	new_dlen = port_newdata - newdata;
402	memcpy(data, newdata, new_dlen);
403
404	SetAckModified(lnk);
405	delta = GetDeltaSeqOut(pip, lnk);
406	AddSeq(pip, lnk, delta + new_dlen - dlen);
407
408	new_len = htons(hlen + new_dlen);
409	DifferentialChecksum(&pip->ip_sum,
410	    &new_len,
411	    &pip->ip_len,
412	    1);
413	pip->ip_len = new_len;
414
415	tc->th_sum = 0;
416#ifdef _KERNEL
417	tc->th_x2 = 1;
418#else
419	tc->th_sum = TcpChecksum(pip);
420#endif
421	return (0);
422}
423
424/* Support the protocol used by early versions of RealPlayer */
425
426static int
427alias_pna_out(struct libalias *la, struct ip *pip,
428    struct alias_link *lnk,
429    char *data,
430    int dlen)
431{
432	struct alias_link *pna_links;
433	u_short msg_id, msg_len;
434	char *work;
435	u_short alias_port, port;
436	struct tcphdr *tc;
437
438	work = data;
439	work += 5;
440	while (work + 4 < data + dlen) {
441		memcpy(&msg_id, work, 2);
442		work += 2;
443		memcpy(&msg_len, work, 2);
444		work += 2;
445		if (ntohs(msg_id) == 0) {
446			/* end of options */
447			return (0);
448		}
449		if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
450			memcpy(&port, work, 2);
451			pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
452			    port, 0, IPPROTO_UDP, 1);
453			if (pna_links != NULL) {
454#ifndef NO_FW_PUNCH
455				/* Punch hole in firewall */
456				PunchFWHole(pna_links);
457#endif
458				tc = (struct tcphdr *)ip_next(pip);
459				alias_port = GetAliasPort(pna_links);
460				memcpy(work, &alias_port, 2);
461
462				/* Compute TCP checksum for revised packet */
463				tc->th_sum = 0;
464#ifdef _KERNEL
465				tc->th_x2 = 1;
466#else
467				tc->th_sum = TcpChecksum(pip);
468#endif
469			}
470		}
471		work += ntohs(msg_len);
472	}
473
474	return (0);
475}
476
477static void
478AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
479{
480	int hlen, tlen, dlen;
481	struct tcphdr *tc;
482	char *data;
483	const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
484	const char *okstr = "OK", *client_port_str = "client_port";
485	const char *server_port_str = "server_port";
486	int i, parseOk;
487
488	(void)maxpacketsize;
489
490	tc = (struct tcphdr *)ip_next(pip);
491	hlen = (pip->ip_hl + tc->th_off) << 2;
492	tlen = ntohs(pip->ip_len);
493	dlen = tlen - hlen;
494
495	data = (char *)pip;
496	data += hlen;
497
498	/* When aliasing a client, check for the SETUP request */
499	if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
500	    (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
501
502		if (dlen >= (int)strlen(setup)) {
503			if (memcmp(data, setup, strlen(setup)) == 0) {
504				alias_rtsp_out(la, pip, lnk, data, client_port_str);
505				return;
506			}
507		}
508		if (dlen >= (int)strlen(pna)) {
509			if (memcmp(data, pna, strlen(pna)) == 0) {
510				alias_pna_out(la, pip, lnk, data, dlen);
511			}
512		}
513	} else {
514
515		/*
516		 * When aliasing a server, check for the 200 reply
517		 * Accomodate varying number of blanks between 200 & OK
518		 */
519
520		if (dlen >= (int)strlen(str200)) {
521
522			for (parseOk = 0, i = 0;
523			    i <= dlen - (int)strlen(str200);
524			    i++) {
525				if (memcmp(&data[i], str200, strlen(str200)) == 0) {
526					parseOk = 1;
527					break;
528				}
529			}
530			if (parseOk) {
531
532				i += strlen(str200);	/* skip string found */
533				while (data[i] == ' ')	/* skip blank(s) */
534					i++;
535
536				if ((dlen - i) >= (int)strlen(okstr)) {
537
538					if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
539						alias_rtsp_out(la, pip, lnk, data, server_port_str);
540
541				}
542			}
543		}
544	}
545}
546