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