- include sys/param.h for endian macros
[dragonfly.git] / lib / 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  * $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 00:08:17 joerg Exp $
67  */
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 #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>
109
110 #include "alias_local.h"
111
112 #define RTSP_CONTROL_PORT_NUMBER_1 554 
113 #define RTSP_CONTROL_PORT_NUMBER_2 7070 
114 #define RTSP_PORT_GROUP            2
115
116 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
117
118 static int
119 search_string(char *data, int dlen, const char *search_str)
120 {
121     int i, j, k;
122     int search_str_len;
123
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;
130             }
131             if (k == search_str_len - 1) {
132                 return j + 1;
133             }
134         }
135     }
136     return -1;
137 }
138
139 static int
140 alias_rtsp_out(struct ip *pip,
141                    struct alias_link *link,
142                    char *data,
143                    const char *port_str)
144 {
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; 
156
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;
162
163     /* Find keyword, "Transport: " */
164     pos = search_string(data, dlen, transport_str);
165     if (pos < 0) {
166         return -1;
167     }
168     port_data = data + pos;
169     port_dlen = dlen - pos;
170
171     memcpy(newdata, data, pos);
172     port_newdata = newdata + pos;
173
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;
179         }
180
181         memcpy (port_newdata, port_data, pos + 1);
182         port_newdata += (pos + 1);
183
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++;
192                 }
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;
200                     }
201                     if (port_data[i] == '-') {
202                         state++;
203                     }
204                 }
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++;
211                 }
212                 break;
213             case 3:
214                 base_port = p[0];
215                 sport = htons(p[0]);
216                 eport = htons(p[1]);
217
218                 if (!links_created) {
219
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 {
234
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;
252                       }
253                     }
254                   }
255                   ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
256                 }
257
258                 if (salias && rtsp_link) {
259
260                   pkt_updated = 1;
261
262                   /* Copy into IP packet */
263                   sprintf(stemp, "%d", ntohs(salias));
264                   memcpy(port_newdata, stemp, strlen(stemp));
265                   port_newdata += strlen(stemp);
266
267                   if (eport != 0) {
268                     *port_newdata = '-';
269                     port_newdata++;
270
271                     /* Copy into IP packet */
272                     sprintf(stemp, "%d", ntohs(ealias));
273                     memcpy(port_newdata, stemp, strlen(stemp));
274                     port_newdata += strlen(stemp);
275                   }
276
277                   *port_newdata = ';';
278                   port_newdata++;
279                 }
280                 state++;
281                 break;
282             }
283             if (state > 3) {
284                 break;
285             }
286         }
287         port_data += i;
288         port_dlen -= i;
289     }
290
291     if (!pkt_updated)
292       return -1;
293
294     memcpy (port_newdata, port_data, port_dlen);
295     port_newdata += port_dlen;
296     *port_newdata = '\0';
297
298     /* Create new packet */
299     new_dlen = port_newdata - newdata;
300     memcpy (data, newdata, new_dlen);
301
302     SetAckModified(link);
303     delta = GetDeltaSeqOut(pip, link);
304     AddSeq(pip, link, delta + new_dlen - dlen);
305
306     new_len = htons(hlen + new_dlen);
307     DifferentialChecksum(&pip->ip_sum,
308                          &new_len,
309                          &pip->ip_len,
310                          1);
311     pip->ip_len = new_len;
312
313     tc->th_sum = 0;
314     tc->th_sum = TcpChecksum(pip);
315
316     return 0;
317 }
318
319 /* Support the protocol used by early versions of RealPlayer */
320
321 static int
322 alias_pna_out(struct ip *pip,
323                   struct alias_link *link,
324                   char *data,
325                   int dlen)
326 {
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;
332
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;
343         }
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);
356
357                 /* Compute TCP checksum for revised packet */
358                 tc->th_sum = 0;
359                 tc->th_sum = TcpChecksum(pip);
360             }
361         }
362         work += ntohs(msg_len);
363     }
364     
365     return 0;
366 }
367
368 void
369 AliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize)
370 {
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;
378
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;
383
384     data = (char*)pip;
385     data += hlen;
386
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)) { 
390
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;
395         }
396       }
397       if (dlen >= strlen(pna)) {
398         if (memcmp(data, pna, strlen(pna)) == 0) {
399             alias_pna_out(pip, link, data, dlen);
400         }
401       }
402
403     } else {
404
405       /* When aliasing a server, check for the 200 reply
406          Accomodate varying number of blanks between 200 & OK */
407
408       if (dlen >= strlen(str200)) {
409
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;
416           }
417         }
418         if (parseOk) { 
419
420           i += strlen(str200);        /* skip string found */ 
421           while(data[i] == ' ')       /* skip blank(s) */
422             i++;
423         
424           if ((dlen - i) >= strlen(okstr)) {
425
426             if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 
427               alias_rtsp_out(pip, link, data, server_port_str);
428
429           }
430         }
431       }
432     }
433 }