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