]> git.saurik.com Git - apple/network_cmds.git/blob - alias/alias_smedia.c
fad6f78cb10985aa4356cec412e33dce2a7c67a7
[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
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 if (dlen > sizeof(newdata)) {
186 #if DEBUG
187 fprintf(stderr, "alias_rtsp_out: data len too big");
188 #endif
189 return -1;
190 }
191 /* Find keyword, "Transport: " */
192 pos = search_string(data, dlen, transport_str);
193 if (pos < 0) {
194 #if DEBUG
195 fprintf(stderr, "alias_rtsp_out: Transport not found");
196 #endif
197 return -1;
198 }
199 if (pos >= sizeof(newdata)) {
200 #if DEBUG
201 fprintf(stderr, "alias_rtsp_out: Transport too far");
202 #endif
203 return -1;
204 }
205 port_data = data + pos;
206 port_dlen = dlen - pos;
207
208 memcpy(newdata, data, pos);
209 port_newdata = newdata + pos;
210
211 while (port_dlen > strlen(port_str)) {
212 /* Find keyword, appropriate port string */
213 pos = search_string(port_data, port_dlen, port_str);
214 if (pos < 0 || port_data + pos + 1 > newdata + sizeof(newdata)) {
215 #if DEBUG
216 fprintf(stderr, "alias_rtsp_out: port_str too far\n");
217 #endif
218 break;
219 }
220
221 memcpy (port_newdata, port_data, pos + 1);
222 port_newdata += (pos + 1);
223
224 p[0] = p[1] = 0;
225 sport = eport = 0;
226 state = 0;
227 for (i = pos; i < port_dlen; i++) {
228 switch(state) {
229 case 0:
230 if (port_data[i] == '=') {
231 state++;
232 }
233 break;
234 case 1:
235 if (ISDIGIT(port_data[i])) {
236 p[0] = p[0] * 10 + port_data[i] - '0';
237 } else {
238 if (port_data[i] == ';') {
239 state = 3;
240 }
241 if (port_data[i] == '-') {
242 state++;
243 }
244 }
245 break;
246 case 2:
247 if (ISDIGIT(port_data[i])) {
248 p[1] = p[1] * 10 + port_data[i] - '0';
249 } else {
250 state++;
251 }
252 break;
253 case 3:
254 base_port = p[0];
255 sport = htons(p[0]);
256 eport = htons(p[1]);
257
258 if (!links_created) {
259
260 links_created = 1;
261 /* Find an even numbered port number base that
262 satisfies the contiguous number of ports we need */
263 null_addr.s_addr = 0;
264 if (0 == (salias = FindNewPortGroup(null_addr,
265 FindAliasAddress(pip->ip_src),
266 sport, 0,
267 RTSP_PORT_GROUP,
268 IPPROTO_UDP, 1))) {
269 #ifdef DEBUG
270 fprintf(stderr,
271 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
272 #endif
273 } else {
274
275 base_alias = ntohs(salias);
276 for (j = 0; j < RTSP_PORT_GROUP; j++) {
277 /* Establish link to port found in RTSP packet */
278 rtsp_link = FindRtspOut(GetOriginalAddress(link), null_addr,
279 htons(base_port + j), htons(base_alias + j),
280 IPPROTO_UDP);
281 if (rtsp_link != NULL) {
282 #ifndef NO_FW_PUNCH
283 /* Punch hole in firewall */
284 PunchFWHole(rtsp_link);
285 #endif
286 } else {
287 #ifdef DEBUG
288 fprintf(stderr,
289 "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
290 #endif
291 break;
292 }
293 }
294 }
295 ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
296 }
297
298 if (salias && rtsp_link) {
299 size_t lentmp;
300
301 /* Copy into IP packet */
302 sprintf(stemp, "%d", ntohs(salias));
303 lentmp = strlen(stemp);
304 /* account for ending ';' */
305 if (port_newdata + lentmp + 1 > newdata + sizeof(newdata)) {
306 #if DEBUG
307 fprintf(stderr, "PacketAlias/RTSP: salias too far\n");
308 #endif
309 break;
310 }
311 memcpy(port_newdata, stemp, lentmp);
312 port_newdata += lentmp;
313
314 if (eport != 0) {
315 sprintf(stemp, "%d", ntohs(ealias));
316 lentmp = strlen(stemp);
317
318 /* account for middle '-' and for ending ';' */
319 if (port_newdata + lentmp + 2 > newdata + sizeof(newdata)) {
320 #if DEBUG
321 fprintf(stderr, "PacketAlias/RTSP: ealias too far\n");
322 #endif
323 break;
324 }
325 *port_newdata = '-';
326 port_newdata++;
327
328 /* Copy into IP packet */
329 memcpy(port_newdata, stemp, lentmp);
330 port_newdata += lentmp;
331 }
332 pkt_updated = 1;
333
334 *port_newdata = ';';
335 port_newdata++;
336 }
337 state++;
338 break;
339 }
340 if (state > 3) {
341 break;
342 }
343 }
344 port_data += i;
345 port_dlen -= i;
346 }
347
348 if (!pkt_updated) {
349 #if DEBUG
350 fprintf(stderr, "PacketAlias/RTSP: Packet not updated\n");
351 #endif
352 return -1;
353 }
354
355 memcpy (port_newdata, port_data, port_dlen);
356 port_newdata += port_dlen;
357 *port_newdata = '\0';
358
359 /* Create new packet */
360 new_dlen = port_newdata - newdata;
361 memcpy (data, newdata, new_dlen);
362
363 SetAckModified(link);
364 delta = GetDeltaSeqOut(pip, link);
365 AddSeq(pip, link, delta + new_dlen - dlen);
366
367 new_len = htons(hlen + new_dlen);
368 DifferentialChecksum(&pip->ip_sum,
369 &new_len,
370 &pip->ip_len,
371 1);
372 pip->ip_len = new_len;
373
374 tc->th_sum = 0;
375 tc->th_sum = TcpChecksum(pip);
376
377 return 0;
378 }
379
380 /* Support the protocol used by early versions of RealPlayer */
381
382 static int
383 alias_pna_out(struct ip *pip,
384 struct alias_link *link,
385 char *data,
386 int dlen)
387 {
388 struct alias_link *pna_links;
389 u_short msg_id, msg_len;
390 char *work;
391 u_short alias_port, port;
392 struct tcphdr *tc;
393
394 work = data;
395 work += 5;
396 while (work + 4 < data + dlen) {
397 memcpy(&msg_id, work, 2);
398 work += 2;
399 memcpy(&msg_len, work, 2);
400 work += 2;
401 if (ntohs(msg_id) == 0) {
402 /* end of options */
403 return 0;
404 }
405 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
406 memcpy(&port, work, 2);
407 pna_links = FindUdpTcpOut(pip->ip_src, GetDestAddress(link),
408 port, 0, IPPROTO_UDP, 1);
409 if (pna_links != NULL) {
410 #ifndef NO_FW_PUNCH
411 /* Punch hole in firewall */
412 PunchFWHole(pna_links);
413 #endif
414 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
415 alias_port = GetAliasPort(pna_links);
416 memcpy(work, &alias_port, 2);
417
418 /* Compute TCP checksum for revised packet */
419 tc->th_sum = 0;
420 tc->th_sum = TcpChecksum(pip);
421 }
422 }
423 work += ntohs(msg_len);
424 }
425
426 return 0;
427 }
428
429 void
430 AliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize)
431 {
432 int hlen, tlen, dlen;
433 struct tcphdr *tc;
434 char *data;
435 const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
436 const char *okstr = "OK", *client_port_str = "client_port";
437 const char *server_port_str = "server_port";
438 int i, parseOk;
439
440 tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
441 hlen = (pip->ip_hl + tc->th_off) << 2;
442 tlen = ntohs(pip->ip_len);
443 dlen = tlen - hlen;
444
445 data = (char*)pip;
446 data += hlen;
447
448 /* When aliasing a client, check for the SETUP request */
449 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
450 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
451
452 if (dlen >= strlen(setup)) {
453 if (memcmp(data, setup, strlen(setup)) == 0) {
454 alias_rtsp_out(pip, link, data, client_port_str);
455 return;
456 }
457 }
458 if (dlen >= strlen(pna)) {
459 if (memcmp(data, pna, strlen(pna)) == 0) {
460 alias_pna_out(pip, link, data, dlen);
461 }
462 }
463
464 } else {
465
466 /* When aliasing a server, check for the 200 reply
467 Accomodate varying number of blanks between 200 & OK */
468
469 if (dlen >= strlen(str200)) {
470
471 for (parseOk = 0, i = 0;
472 i <= dlen - strlen(str200);
473 i++) {
474 if (memcmp(&data[i], str200, strlen(str200)) == 0) {
475 parseOk = 1;
476 break;
477 }
478 }
479 if (parseOk) {
480
481 i += strlen(str200); /* skip string found */
482 while(data[i] == ' ') /* skip blank(s) */
483 i++;
484
485 if ((dlen - i) >= strlen(okstr)) {
486
487 if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
488 alias_rtsp_out(pip, link, data, server_port_str);
489
490 }
491 }
492 }
493 }
494 }