network_cmds-511.tar.gz
[apple/network_cmds.git] / alias / alias_smedia.c
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
147 static int
148 search_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
168 static int
169 alias_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
389 static int
390 alias_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
436 void
437 AliasHandleRtspOut(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 }