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