]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/netinet/libalias/alias_smedia.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / sys / netinet / libalias / alias_smedia.c
1 /*
2  * alias_smedia.c
3  *
4  * Copyright (c) 2000 Whistle Communications, Inc.
5  * All rights reserved.
6  *
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.
17  *
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.
35  *
36  * Copyright (c) 2000  Junichi SATOH <junichi@astec.co.jp>
37  *                                   <junichi@junichi.org>
38  * All rights reserved.
39  *
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.
48  *
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.
60  *
61  * Authors: Erik Salander <erik@whistle.com>
62  *          Junichi SATOH <junichi@astec.co.jp>
63  *                        <junichi@junichi.org>
64  */
65
66 #include <sys/cdefs.h>
67 __FBSDID("$FreeBSD$");
68
69 /*
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.
75
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.
80
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.
85
86    The RTSP multicast functions without any address translation intervention.
87
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.
91
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.
97
98    Initial version:  May, 2000 (eds)
99 */
100
101 #ifdef _KERNEL
102 #include <sys/param.h>
103 #include <sys/libkern.h>
104 #else
105 #include <sys/types.h>
106 #include <stdio.h>
107 #include <string.h>
108 #endif
109
110 #include <netinet/in_systm.h>
111 #include <netinet/in.h>
112 #include <netinet/ip.h>
113 #include <netinet/tcp.h>
114 #include <netinet/udp.h>
115
116 #ifdef _KERNEL
117 #include <netinet/libalias/alias.h>
118 #include <netinet/libalias/alias_local.h>
119 #else
120 #include "alias_local.h"
121 #endif
122
123 #define RTSP_CONTROL_PORT_NUMBER_1 554
124 #define RTSP_CONTROL_PORT_NUMBER_2 7070
125 #define RTSP_PORT_GROUP            2
126
127 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
128
129 static int
130 search_string(char *data, int dlen, const char *search_str)
131 {
132         int i, j, k;
133         int search_str_len;
134
135         search_str_len = strlen(search_str);
136         for (i = 0; i < dlen - search_str_len; i++) {
137                 for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
138                         if (data[j] != search_str[k] &&
139                             data[j] != search_str[k] - ('a' - 'A')) {
140                                 break;
141                         }
142                         if (k == search_str_len - 1) {
143                                 return (j + 1);
144                         }
145                 }
146         }
147         return (-1);
148 }
149
150 static int
151 alias_rtsp_out(struct libalias *la, struct ip *pip,
152     struct alias_link *lnk,
153     char *data,
154     const char *port_str)
155 {
156         int hlen, tlen, dlen;
157         struct tcphdr *tc;
158         int i, j, pos, state, port_dlen, new_dlen, delta;
159         u_short p[2], new_len;
160         u_short sport, eport, base_port;
161         u_short salias = 0, ealias = 0, base_alias = 0;
162         const char *transport_str = "transport:";
163         char newdata[2048], *port_data, *port_newdata, stemp[80];
164         int links_created = 0, pkt_updated = 0;
165         struct alias_link *rtsp_lnk = NULL;
166         struct in_addr null_addr;
167
168         /* Calculate data length of TCP packet */
169         tc = (struct tcphdr *)ip_next(pip);
170         hlen = (pip->ip_hl + tc->th_off) << 2;
171         tlen = ntohs(pip->ip_len);
172         dlen = tlen - hlen;
173
174         /* Find keyword, "Transport: " */
175         pos = search_string(data, dlen, transport_str);
176         if (pos < 0) {
177                 return (-1);
178         }
179         port_data = data + pos;
180         port_dlen = dlen - pos;
181
182         memcpy(newdata, data, pos);
183         port_newdata = newdata + pos;
184
185         while (port_dlen > (int)strlen(port_str)) {
186                 /* Find keyword, appropriate port string */
187                 pos = search_string(port_data, port_dlen, port_str);
188                 if (pos < 0) {
189                         break;
190                 }
191                 memcpy(port_newdata, port_data, pos + 1);
192                 port_newdata += (pos + 1);
193
194                 p[0] = p[1] = 0;
195                 sport = eport = 0;
196                 state = 0;
197                 for (i = pos; i < port_dlen; i++) {
198                         switch (state) {
199                         case 0:
200                                 if (port_data[i] == '=') {
201                                         state++;
202                                 }
203                                 break;
204                         case 1:
205                                 if (ISDIGIT(port_data[i])) {
206                                         p[0] = p[0] * 10 + port_data[i] - '0';
207                                 } else {
208                                         if (port_data[i] == ';') {
209                                                 state = 3;
210                                         }
211                                         if (port_data[i] == '-') {
212                                                 state++;
213                                         }
214                                 }
215                                 break;
216                         case 2:
217                                 if (ISDIGIT(port_data[i])) {
218                                         p[1] = p[1] * 10 + port_data[i] - '0';
219                                 } else {
220                                         state++;
221                                 }
222                                 break;
223                         case 3:
224                                 base_port = p[0];
225                                 sport = htons(p[0]);
226                                 eport = htons(p[1]);
227
228                                 if (!links_created) {
229
230                                         links_created = 1;
231                                         /*
232                                          * Find an even numbered port
233                                          * number base that satisfies the
234                                          * contiguous number of ports we
235                                          * need
236                                          */
237                                         null_addr.s_addr = 0;
238                                         if (0 == (salias = FindNewPortGroup(la, null_addr,
239                                             FindAliasAddress(la, pip->ip_src),
240                                             sport, 0,
241                                             RTSP_PORT_GROUP,
242                                             IPPROTO_UDP, 1))) {
243 #ifdef LIBALIAS_DEBUG
244                                                 fprintf(stderr,
245                                                     "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
246 #endif
247                                         } else {
248
249                                                 base_alias = ntohs(salias);
250                                                 for (j = 0; j < RTSP_PORT_GROUP; j++) {
251                                                         /*
252                                                          * Establish link
253                                                          * to port found in
254                                                          * RTSP packet
255                                                          */
256                                                         rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
257                                                             htons(base_port + j), htons(base_alias + j),
258                                                             IPPROTO_UDP);
259                                                         if (rtsp_lnk != NULL) {
260 #ifndef NO_FW_PUNCH
261                                                                 /*
262                                                                  * Punch
263                                                                  * hole in
264                                                                  * firewall
265                                                                  */
266                                                                 PunchFWHole(rtsp_lnk);
267 #endif
268                                                         } else {
269 #ifdef LIBALIAS_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                                 if (salias && rtsp_lnk) {
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                                         *port_newdata = ';';
298                                         port_newdata++;
299                                 }
300                                 state++;
301                                 break;
302                         }
303                         if (state > 3) {
304                                 break;
305                         }
306                 }
307                 port_data += i;
308                 port_dlen -= i;
309         }
310
311         if (!pkt_updated)
312                 return (-1);
313
314         memcpy(port_newdata, port_data, port_dlen);
315         port_newdata += port_dlen;
316         *port_newdata = '\0';
317
318         /* Create new packet */
319         new_dlen = port_newdata - newdata;
320         memcpy(data, newdata, new_dlen);
321
322         SetAckModified(lnk);
323         delta = GetDeltaSeqOut(pip, lnk);
324         AddSeq(pip, lnk, delta + new_dlen - dlen);
325
326         new_len = htons(hlen + new_dlen);
327         DifferentialChecksum(&pip->ip_sum,
328             &new_len,
329             &pip->ip_len,
330             1);
331         pip->ip_len = new_len;
332
333         tc->th_sum = 0;
334 #ifdef _KERNEL
335         tc->th_x2 = 1;
336 #else
337         tc->th_sum = TcpChecksum(pip);
338 #endif
339         return (0);
340 }
341
342 /* Support the protocol used by early versions of RealPlayer */
343
344 static int
345 alias_pna_out(struct libalias *la, struct ip *pip,
346     struct alias_link *lnk,
347     char *data,
348     int dlen)
349 {
350         struct alias_link *pna_links;
351         u_short msg_id, msg_len;
352         char *work;
353         u_short alias_port, port;
354         struct tcphdr *tc;
355
356         work = data;
357         work += 5;
358         while (work + 4 < data + dlen) {
359                 memcpy(&msg_id, work, 2);
360                 work += 2;
361                 memcpy(&msg_len, work, 2);
362                 work += 2;
363                 if (ntohs(msg_id) == 0) {
364                         /* end of options */
365                         return (0);
366                 }
367                 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
368                         memcpy(&port, work, 2);
369                         pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
370                             port, 0, IPPROTO_UDP, 1);
371                         if (pna_links != NULL) {
372 #ifndef NO_FW_PUNCH
373                                 /* Punch hole in firewall */
374                                 PunchFWHole(pna_links);
375 #endif
376                                 tc = (struct tcphdr *)ip_next(pip);
377                                 alias_port = GetAliasPort(pna_links);
378                                 memcpy(work, &alias_port, 2);
379
380                                 /* Compute TCP checksum for revised packet */
381                                 tc->th_sum = 0;
382 #ifdef _KERNEL
383                                 tc->th_x2 = 1;
384 #else
385                                 tc->th_sum = TcpChecksum(pip);
386 #endif
387                         }
388                 }
389                 work += ntohs(msg_len);
390         }
391
392         return (0);
393 }
394
395 void
396 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
397 {
398         int hlen, tlen, dlen;
399         struct tcphdr *tc;
400         char *data;
401         const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
402         const char *okstr = "OK", *client_port_str = "client_port";
403         const char *server_port_str = "server_port";
404         int i, parseOk;
405
406         (void)maxpacketsize;
407
408         tc = (struct tcphdr *)ip_next(pip);
409         hlen = (pip->ip_hl + tc->th_off) << 2;
410         tlen = ntohs(pip->ip_len);
411         dlen = tlen - hlen;
412
413         data = (char *)pip;
414         data += hlen;
415
416         /* When aliasing a client, check for the SETUP request */
417         if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
418             (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
419
420                 if (dlen >= (int)strlen(setup)) {
421                         if (memcmp(data, setup, strlen(setup)) == 0) {
422                                 alias_rtsp_out(la, pip, lnk, data, client_port_str);
423                                 return;
424                         }
425                 }
426                 if (dlen >= (int)strlen(pna)) {
427                         if (memcmp(data, pna, strlen(pna)) == 0) {
428                                 alias_pna_out(la, pip, lnk, data, dlen);
429                         }
430                 }
431         } else {
432
433                 /*
434                  * When aliasing a server, check for the 200 reply
435                  * Accomodate varying number of blanks between 200 & OK
436                  */
437
438                 if (dlen >= (int)strlen(str200)) {
439
440                         for (parseOk = 0, i = 0;
441                             i <= dlen - (int)strlen(str200);
442                             i++) {
443                                 if (memcmp(&data[i], str200, strlen(str200)) == 0) {
444                                         parseOk = 1;
445                                         break;
446                                 }
447                         }
448                         if (parseOk) {
449
450                                 i += strlen(str200);    /* skip string found */
451                                 while (data[i] == ' ')  /* skip blank(s) */
452                                         i++;
453
454                                 if ((dlen - i) >= (int)strlen(okstr)) {
455
456                                         if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
457                                                 alias_rtsp_out(la, pip, lnk, data, server_port_str);
458
459                                 }
460                         }
461                 }
462         }
463 }