1/*
2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * alias_smedia.c
31 *
32 * Copyright (c) 2000 Whistle Communications, Inc.
33 * All rights reserved.
34 *
35 * Subject to the following obligations and disclaimer of warranty, use and
36 * redistribution of this software, in source or object code forms, with or
37 * without modifications are expressly permitted by Whistle Communications;
38 * provided, however, that:
39 * 1. Any and all reproductions of the source or object code must include the
40 *    copyright notice above and the following disclaimer of warranties; and
41 * 2. No rights are granted, in any manner or form, to use Whistle
42 *    Communications, Inc. trademarks, including the mark "WHISTLE
43 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
44 *    such appears in the above copyright notice or in the software.
45 *
46 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
47 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
48 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
49 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
50 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
51 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
52 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
53 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
54 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
55 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
56 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
57 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
58 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
59 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
61 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
62 * OF SUCH DAMAGE.
63 *
64 * Copyright (c) 2000  Junichi SATOH <junichi@astec.co.jp>
65 *                                   <junichi@junichi.org>
66 * All rights reserved.
67 *
68 * Redistribution and use in source and binary forms, with or without
69 * modification, are permitted provided that the following conditions
70 * are met:
71 * 1. Redistributions of source code must retain the above copyright
72 *    notice, this list of conditions and the following disclaimer.
73 * 2. Redistributions in binary form must reproduce the above copyright
74 *    notice, this list of conditions and the following disclaimer in the
75 *    documentation and/or other materials provided with the distribution.
76 *
77 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
87 * SUCH DAMAGE.
88 *
89 * Authors: Erik Salander <erik@whistle.com>
90 *          Junichi SATOH <junichi@astec.co.jp>
91 *                        <junichi@junichi.org>
92 *
93 * Based upon:
94 * $FreeBSD: src/lib/libalias/alias_smedia.c,v 1.1.2.4 2001/03/05 03:48:00 kris Exp $
95 */
96
97/*
98   Alias_smedia.c is meant to contain the aliasing code for streaming media
99   protocols.  It performs special processing for RSTP sessions under TCP.
100   Specifically, when a SETUP request is sent by a client, or a 200 reply
101   is sent by a server, it is intercepted and modified.  The address is
102   changed to the gateway machine and an aliasing port is used.
103
104   More specifically, the "client_port" configuration parameter is
105   parsed for SETUP requests.  The "server_port" configuration parameter is
106   parsed for 200 replies eminating from a server.  This is intended to handle
107   the unicast case.
108
109   RTSP also allows a redirection of a stream to another client by using the
110   "destination" configuration parameter.  The destination config parm would
111   indicate a different IP address.  This function is NOT supported by the
112   RTSP translation code below.
113
114   The RTSP multicast functions without any address translation intervention.
115
116   For this routine to work, the SETUP/200 must fit entirely
117   into a single TCP packet.  This is typically the case, but exceptions
118   can easily be envisioned under the actual specifications.
119
120   Probably the most troubling aspect of the approach taken here is
121   that the new SETUP/200 will typically be a different length, and
122   this causes a certain amount of bookkeeping to keep track of the
123   changes of sequence and acknowledgment numbers, since the client
124   machine is totally unaware of the modification to the TCP stream.
125
126   Initial version:  May, 2000 (eds)
127*/
128
129
130#include <stdio.h>
131#include <string.h>
132#include <sys/types.h>
133#include <netinet/in_systm.h>
134#include <netinet/in.h>
135#include <netinet/ip.h>
136#include <netinet/tcp.h>
137#include <netinet/udp.h>
138
139#include "alias_local.h"
140
141#define RTSP_CONTROL_PORT_NUMBER_1 554
142#define RTSP_CONTROL_PORT_NUMBER_2 7070
143#define RTSP_PORT_GROUP            2
144
145#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
146
147static int
148search_string(char *data, int dlen, const char *search_str)
149{
150    int i, j, k;
151    int search_str_len;
152
153    search_str_len = strlen(search_str);
154    for (i = 0; i < dlen - search_str_len; i++) {
155	for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
156	    if (data[j] != search_str[k] &&
157		data[j] != search_str[k] - ('a' - 'A')) {
158		break;
159	    }
160	    if (k == search_str_len - 1) {
161		return j + 1;
162	    }
163	}
164    }
165    return -1;
166}
167
168static int
169alias_rtsp_out(struct ip *pip,
170		   struct alias_link *link,
171		   char *data,
172		   const char *port_str)
173{
174    int     hlen, tlen, dlen;
175    struct tcphdr *tc;
176    int     i, j, pos, state, port_dlen, new_dlen, delta;
177    u_short p[2], new_len;
178    u_short sport, eport, base_port;
179    u_short salias = 0, ealias = 0, base_alias = 0;
180    const char *transport_str = "transport:";
181    char    newdata[2048], *port_data, *port_newdata, stemp[80];
182    int     links_created = 0, pkt_updated = 0;
183    struct alias_link *rtsp_link = NULL;
184    struct in_addr null_addr;
185
186    /* Calculate data length of TCP packet */
187    tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
188    hlen = (pip->ip_hl + tc->th_off) << 2;
189    tlen = ntohs(pip->ip_len);
190    dlen = tlen - hlen;
191
192	if (dlen > sizeof(newdata)) {
193#if DEBUG
194		fprintf(stderr, "alias_rtsp_out: data len too big");
195#endif
196		return -1;
197	}
198    /* Find keyword, "Transport: " */
199    pos = search_string(data, dlen, transport_str);
200    if (pos < 0) {
201#if DEBUG
202		fprintf(stderr, "alias_rtsp_out: Transport not found");
203#endif
204		return -1;
205    }
206	if (pos >= sizeof(newdata)) {
207#if DEBUG
208		fprintf(stderr, "alias_rtsp_out: Transport too far");
209#endif
210		return -1;
211	}
212    port_data = data + pos;
213    port_dlen = dlen - pos;
214
215    memcpy(newdata, data, pos);
216    port_newdata = newdata + pos;
217
218    while (port_dlen > strlen(port_str)) {
219		/* Find keyword, appropriate port string */
220		pos = search_string(port_data, port_dlen, port_str);
221		if (pos < 0 || port_data + pos + 1 > newdata + sizeof(newdata)) {
222#if DEBUG
223			fprintf(stderr, "alias_rtsp_out: port_str too far\n");
224#endif
225			break;
226		}
227
228		memcpy (port_newdata, port_data, pos + 1);
229		port_newdata += (pos + 1);
230
231		p[0] = p[1] = 0;
232		sport = eport = 0;
233		state = 0;
234		for (i = pos; i < port_dlen; i++) {
235			switch(state) {
236			case 0:
237				if (port_data[i] == '=') {
238					state++;
239				}
240				break;
241			case 1:
242				if (ISDIGIT(port_data[i])) {
243					p[0] = p[0] * 10 + port_data[i] - '0';
244				} else {
245					if (port_data[i] == ';') {
246						state = 3;
247					}
248					if (port_data[i] == '-') {
249						state++;
250					}
251				}
252				break;
253			case 2:
254				if (ISDIGIT(port_data[i])) {
255					p[1] = p[1] * 10 + port_data[i] - '0';
256				} else {
257					state++;
258				}
259				break;
260			case 3:
261				base_port = p[0];
262				sport = htons(p[0]);
263				eport = htons(p[1]);
264
265				if (!links_created) {
266
267					links_created = 1;
268					/* Find an even numbered port number base that
269					 satisfies the contiguous number of ports we need  */
270					null_addr.s_addr = 0;
271					if (0 == (salias = FindNewPortGroup(null_addr,
272														FindAliasAddress(pip->ip_src),
273														sport, 0,
274														RTSP_PORT_GROUP,
275														IPPROTO_UDP, 1))) {
276#ifdef DEBUG
277						fprintf(stderr,
278								"PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
279#endif
280					} else {
281
282						base_alias = ntohs(salias);
283						for (j = 0; j < RTSP_PORT_GROUP; j++) {
284							/* Establish link to port found in RTSP packet */
285							rtsp_link = FindRtspOut(GetOriginalAddress(link), null_addr,
286													htons(base_port + j), htons(base_alias + j),
287													IPPROTO_UDP);
288							if (rtsp_link != NULL) {
289#ifndef NO_FW_PUNCH
290								/* Punch hole in firewall */
291								PunchFWHole(rtsp_link);
292#endif
293							} else {
294#ifdef DEBUG
295								fprintf(stderr,
296										"PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
297#endif
298								break;
299							}
300						}
301					}
302					ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
303				}
304
305				if (salias && rtsp_link) {
306					size_t lentmp;
307
308					/* Copy into IP packet */
309					snprintf(stemp, sizeof(stemp), "%d", ntohs(salias));
310					lentmp = strlen(stemp);
311					/* account for ending ';' */
312					if (port_newdata + lentmp + 1 > newdata + sizeof(newdata)) {
313#if DEBUG
314						fprintf(stderr, "PacketAlias/RTSP: salias too far\n");
315#endif
316						break;
317					}
318					memcpy(port_newdata, stemp, lentmp);
319					port_newdata += lentmp;
320
321					if (eport != 0) {
322						snprintf(stemp, sizeof(stemp), "%d", ntohs(ealias));
323						lentmp = strlen(stemp);
324
325						/* account for middle '-' and for ending ';' */
326						if (port_newdata + lentmp + 2 > newdata + sizeof(newdata)) {
327#if DEBUG
328							fprintf(stderr, "PacketAlias/RTSP: ealias too far\n");
329#endif
330							break;
331						}
332						*port_newdata = '-';
333						port_newdata++;
334
335						/* Copy into IP packet */
336						memcpy(port_newdata, stemp, lentmp);
337						port_newdata += lentmp;
338					}
339					pkt_updated = 1;
340
341					*port_newdata = ';';
342					port_newdata++;
343				}
344				state++;
345				break;
346			}
347			if (state > 3) {
348				break;
349			}
350		}
351		port_data += i;
352		port_dlen -= i;
353    }
354
355    if (!pkt_updated) {
356#if DEBUG
357		fprintf(stderr, "PacketAlias/RTSP: Packet not updated\n");
358#endif
359		return -1;
360	}
361
362    memcpy (port_newdata, port_data, port_dlen);
363    port_newdata += port_dlen;
364    *port_newdata = '\0';
365
366    /* Create new packet */
367    new_dlen = port_newdata - newdata;
368    memcpy (data, newdata, new_dlen);
369
370    SetAckModified(link);
371    delta = GetDeltaSeqOut(pip, link);
372    AddSeq(pip, link, delta + new_dlen - dlen);
373
374    new_len = htons(hlen + new_dlen);
375    DifferentialChecksum(&pip->ip_sum,
376						 &new_len,
377						 &pip->ip_len,
378						 1);
379    pip->ip_len = new_len;
380
381    tc->th_sum = 0;
382    tc->th_sum = TcpChecksum(pip);
383
384    return 0;
385}
386
387/* Support the protocol used by early versions of RealPlayer */
388
389static int
390alias_pna_out(struct ip *pip,
391		  struct alias_link *link,
392		  char *data,
393		  int dlen)
394{
395    struct alias_link *pna_links;
396    u_short msg_id, msg_len;
397    char    *work;
398    u_short alias_port, port;
399    struct  tcphdr *tc;
400
401    work = data;
402    work += 5;
403    while (work + 4 < data + dlen) {
404	memcpy(&msg_id, work, 2);
405	work += 2;
406	memcpy(&msg_len, work, 2);
407	work += 2;
408	if (ntohs(msg_id) == 0) {
409	    /* end of options */
410	    return 0;
411	}
412	if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
413	    memcpy(&port, work, 2);
414	    pna_links = FindUdpTcpOut(pip->ip_src, GetDestAddress(link),
415				      port, 0, IPPROTO_UDP, 1);
416	    if (pna_links != NULL) {
417#ifndef NO_FW_PUNCH
418		/* Punch hole in firewall */
419		PunchFWHole(pna_links);
420#endif
421		tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
422		alias_port = GetAliasPort(pna_links);
423		memcpy(work, &alias_port, 2);
424
425		/* Compute TCP checksum for revised packet */
426		tc->th_sum = 0;
427		tc->th_sum = TcpChecksum(pip);
428	    }
429	}
430	work += ntohs(msg_len);
431    }
432
433    return 0;
434}
435
436void
437AliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize)
438{
439    int    hlen, tlen, dlen;
440    struct tcphdr *tc;
441    char   *data;
442    const  char *setup = "SETUP", *pna = "PNA", *str200 = "200";
443    const  char *okstr = "OK", *client_port_str = "client_port";
444    const  char *server_port_str = "server_port";
445    int    i, parseOk;
446
447    tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
448    hlen = (pip->ip_hl + tc->th_off) << 2;
449    tlen = ntohs(pip->ip_len);
450    dlen = tlen - hlen;
451
452    data = (char*)pip;
453    data += hlen;
454
455    /* When aliasing a client, check for the SETUP request */
456    if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
457      (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
458
459      if (dlen >= strlen(setup)) {
460        if (memcmp(data, setup, strlen(setup)) == 0) {
461	    alias_rtsp_out(pip, link, data, client_port_str);
462	    return;
463	}
464      }
465      if (dlen >= strlen(pna)) {
466	if (memcmp(data, pna, strlen(pna)) == 0) {
467	    alias_pna_out(pip, link, data, dlen);
468	}
469      }
470
471    } else {
472
473      /* When aliasing a server, check for the 200 reply
474         Accomodate varying number of blanks between 200 & OK */
475
476      if (dlen >= strlen(str200)) {
477
478        for (parseOk = 0, i = 0;
479             i <= dlen - strlen(str200);
480             i++) {
481          if (memcmp(&data[i], str200, strlen(str200)) == 0) {
482            parseOk = 1;
483            break;
484          }
485        }
486        if (parseOk) {
487
488          i += strlen(str200);        /* skip string found */
489          while(data[i] == ' ')       /* skip blank(s) */
490	    i++;
491
492          if ((dlen - i) >= strlen(okstr)) {
493
494            if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
495              alias_rtsp_out(pip, link, data, server_port_str);
496
497          }
498        }
499      }
500    }
501}
502