]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/libalias/alias_smedia.c
This commit was generated by cvs2svn to compensate for changes in r162911,
[FreeBSD/FreeBSD.git] / 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/systm.h>
104 #include <sys/kernel.h>
105 #include <sys/module.h>
106 #else
107 #include <errno.h>
108 #include <sys/types.h>
109 #include <stdio.h>
110 #include <string.h>
111 #endif
112
113 #include <netinet/in_systm.h>
114 #include <netinet/in.h>
115 #include <netinet/ip.h>
116 #include <netinet/tcp.h>
117
118 #ifdef _KERNEL
119 #include <netinet/libalias/alias.h>
120 #include <netinet/libalias/alias_local.h>
121 #include <netinet/libalias/alias_mod.h>
122 #else
123 #include "alias_local.h"
124 #include "alias_mod.h"
125 #endif
126
127 #define RTSP_CONTROL_PORT_NUMBER_1 554
128 #define RTSP_CONTROL_PORT_NUMBER_2 7070
129 #define TFTP_PORT_NUMBER 69
130
131 static void
132 AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, 
133                   int maxpacketsize);
134 static int 
135 fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
136 {
137
138         if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || 
139             ah->maxpktsize == 0)
140                 return (-1);
141         if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1
142             || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1
143             || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2
144             || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2
145             || ntohs(*ah->dport) == TFTP_PORT_NUMBER)
146                 return (0);
147         return (-1);
148 }
149
150 static int 
151 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
152 {
153         
154         if (ntohs(*ah->dport) == TFTP_PORT_NUMBER)
155                 FindRtspOut(la, pip->ip_src, pip->ip_dst,
156                             *ah->sport, *ah->aport, IPPROTO_UDP);
157         else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize);      
158         return (0);
159 }
160
161 struct proto_handler handlers[] = {
162         { 
163           .pri = 100, 
164           .dir = OUT, 
165           .proto = TCP|UDP,
166           .fingerprint = &fingerprint, 
167           .protohandler = &protohandler
168         }, 
169         { EOH }
170 };
171
172 static int
173 mod_handler(module_t mod, int type, void *data)
174 {
175         int error;
176
177         switch (type) {
178         case MOD_LOAD:
179                 error = 0;
180                 LibAliasAttachHandlers(handlers);
181                 break;
182         case MOD_UNLOAD:
183                 error = 0;
184                 LibAliasDetachHandlers(handlers);
185                 break;
186         default:
187                 error = EINVAL;
188         }
189         return (error);
190 }
191
192 #ifdef _KERNEL
193 static 
194 #endif
195 moduledata_t alias_mod = {
196        "alias_smedia", mod_handler, NULL
197 };
198
199 #ifdef  _KERNEL
200 DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
201 MODULE_VERSION(alias_smedia, 1);
202 MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1);
203 #endif
204
205 #define RTSP_CONTROL_PORT_NUMBER_1 554
206 #define RTSP_CONTROL_PORT_NUMBER_2 7070
207 #define RTSP_PORT_GROUP            2
208
209 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
210
211 static int
212 search_string(char *data, int dlen, const char *search_str)
213 {
214         int i, j, k;
215         int search_str_len;
216
217         search_str_len = strlen(search_str);
218         for (i = 0; i < dlen - search_str_len; i++) {
219                 for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
220                         if (data[j] != search_str[k] &&
221                             data[j] != search_str[k] - ('a' - 'A')) {
222                                 break;
223                         }
224                         if (k == search_str_len - 1) {
225                                 return (j + 1);
226                         }
227                 }
228         }
229         return (-1);
230 }
231
232 static int
233 alias_rtsp_out(struct libalias *la, struct ip *pip,
234     struct alias_link *lnk,
235     char *data,
236     const char *port_str)
237 {
238         int hlen, tlen, dlen;
239         struct tcphdr *tc;
240         int i, j, pos, state, port_dlen, new_dlen, delta;
241         u_short p[2], new_len;
242         u_short sport, eport, base_port;
243         u_short salias = 0, ealias = 0, base_alias = 0;
244         const char *transport_str = "transport:";
245         char newdata[2048], *port_data, *port_newdata, stemp[80];
246         int links_created = 0, pkt_updated = 0;
247         struct alias_link *rtsp_lnk = NULL;
248         struct in_addr null_addr;
249
250         /* Calculate data length of TCP packet */
251         tc = (struct tcphdr *)ip_next(pip);
252         hlen = (pip->ip_hl + tc->th_off) << 2;
253         tlen = ntohs(pip->ip_len);
254         dlen = tlen - hlen;
255
256         /* Find keyword, "Transport: " */
257         pos = search_string(data, dlen, transport_str);
258         if (pos < 0) {
259                 return (-1);
260         }
261         port_data = data + pos;
262         port_dlen = dlen - pos;
263
264         memcpy(newdata, data, pos);
265         port_newdata = newdata + pos;
266
267         while (port_dlen > (int)strlen(port_str)) {
268                 /* Find keyword, appropriate port string */
269                 pos = search_string(port_data, port_dlen, port_str);
270                 if (pos < 0) {
271                         break;
272                 }
273                 memcpy(port_newdata, port_data, pos + 1);
274                 port_newdata += (pos + 1);
275
276                 p[0] = p[1] = 0;
277                 sport = eport = 0;
278                 state = 0;
279                 for (i = pos; i < port_dlen; i++) {
280                         switch (state) {
281                         case 0:
282                                 if (port_data[i] == '=') {
283                                         state++;
284                                 }
285                                 break;
286                         case 1:
287                                 if (ISDIGIT(port_data[i])) {
288                                         p[0] = p[0] * 10 + port_data[i] - '0';
289                                 } else {
290                                         if (port_data[i] == ';') {
291                                                 state = 3;
292                                         }
293                                         if (port_data[i] == '-') {
294                                                 state++;
295                                         }
296                                 }
297                                 break;
298                         case 2:
299                                 if (ISDIGIT(port_data[i])) {
300                                         p[1] = p[1] * 10 + port_data[i] - '0';
301                                 } else {
302                                         state++;
303                                 }
304                                 break;
305                         case 3:
306                                 base_port = p[0];
307                                 sport = htons(p[0]);
308                                 eport = htons(p[1]);
309
310                                 if (!links_created) {
311
312                                         links_created = 1;
313                                         /*
314                                          * Find an even numbered port
315                                          * number base that satisfies the
316                                          * contiguous number of ports we
317                                          * need
318                                          */
319                                         null_addr.s_addr = 0;
320                                         if (0 == (salias = FindNewPortGroup(la, null_addr,
321                                             FindAliasAddress(la, pip->ip_src),
322                                             sport, 0,
323                                             RTSP_PORT_GROUP,
324                                             IPPROTO_UDP, 1))) {
325 #ifdef LIBALIAS_DEBUG
326                                                 fprintf(stderr,
327                                                     "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
328 #endif
329                                         } else {
330
331                                                 base_alias = ntohs(salias);
332                                                 for (j = 0; j < RTSP_PORT_GROUP; j++) {
333                                                         /*
334                                                          * Establish link
335                                                          * to port found in
336                                                          * RTSP packet
337                                                          */
338                                                         rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
339                                                             htons(base_port + j), htons(base_alias + j),
340                                                             IPPROTO_UDP);
341                                                         if (rtsp_lnk != NULL) {
342 #ifndef NO_FW_PUNCH
343                                                                 /*
344                                                                  * Punch
345                                                                  * hole in
346                                                                  * firewall
347                                                                  */
348                                                                 PunchFWHole(rtsp_lnk);
349 #endif
350                                                         } else {
351 #ifdef LIBALIAS_DEBUG
352                                                                 fprintf(stderr,
353                                                                     "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
354 #endif
355                                                                 break;
356                                                         }
357                                                 }
358                                         }
359                                         ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
360                                 }
361                                 if (salias && rtsp_lnk) {
362
363                                         pkt_updated = 1;
364
365                                         /* Copy into IP packet */
366                                         sprintf(stemp, "%d", ntohs(salias));
367                                         memcpy(port_newdata, stemp, strlen(stemp));
368                                         port_newdata += strlen(stemp);
369
370                                         if (eport != 0) {
371                                                 *port_newdata = '-';
372                                                 port_newdata++;
373
374                                                 /* Copy into IP packet */
375                                                 sprintf(stemp, "%d", ntohs(ealias));
376                                                 memcpy(port_newdata, stemp, strlen(stemp));
377                                                 port_newdata += strlen(stemp);
378                                         }
379                                         *port_newdata = ';';
380                                         port_newdata++;
381                                 }
382                                 state++;
383                                 break;
384                         }
385                         if (state > 3) {
386                                 break;
387                         }
388                 }
389                 port_data += i;
390                 port_dlen -= i;
391         }
392
393         if (!pkt_updated)
394                 return (-1);
395
396         memcpy(port_newdata, port_data, port_dlen);
397         port_newdata += port_dlen;
398         *port_newdata = '\0';
399
400         /* Create new packet */
401         new_dlen = port_newdata - newdata;
402         memcpy(data, newdata, new_dlen);
403
404         SetAckModified(lnk);
405         delta = GetDeltaSeqOut(pip, lnk);
406         AddSeq(pip, lnk, delta + new_dlen - dlen);
407
408         new_len = htons(hlen + new_dlen);
409         DifferentialChecksum(&pip->ip_sum,
410             &new_len,
411             &pip->ip_len,
412             1);
413         pip->ip_len = new_len;
414
415         tc->th_sum = 0;
416 #ifdef _KERNEL
417         tc->th_x2 = 1;
418 #else
419         tc->th_sum = TcpChecksum(pip);
420 #endif
421         return (0);
422 }
423
424 /* Support the protocol used by early versions of RealPlayer */
425
426 static int
427 alias_pna_out(struct libalias *la, struct ip *pip,
428     struct alias_link *lnk,
429     char *data,
430     int dlen)
431 {
432         struct alias_link *pna_links;
433         u_short msg_id, msg_len;
434         char *work;
435         u_short alias_port, port;
436         struct tcphdr *tc;
437
438         work = data;
439         work += 5;
440         while (work + 4 < data + dlen) {
441                 memcpy(&msg_id, work, 2);
442                 work += 2;
443                 memcpy(&msg_len, work, 2);
444                 work += 2;
445                 if (ntohs(msg_id) == 0) {
446                         /* end of options */
447                         return (0);
448                 }
449                 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
450                         memcpy(&port, work, 2);
451                         pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
452                             port, 0, IPPROTO_UDP, 1);
453                         if (pna_links != NULL) {
454 #ifndef NO_FW_PUNCH
455                                 /* Punch hole in firewall */
456                                 PunchFWHole(pna_links);
457 #endif
458                                 tc = (struct tcphdr *)ip_next(pip);
459                                 alias_port = GetAliasPort(pna_links);
460                                 memcpy(work, &alias_port, 2);
461
462                                 /* Compute TCP checksum for revised packet */
463                                 tc->th_sum = 0;
464 #ifdef _KERNEL
465                                 tc->th_x2 = 1;
466 #else
467                                 tc->th_sum = TcpChecksum(pip);
468 #endif
469                         }
470                 }
471                 work += ntohs(msg_len);
472         }
473
474         return (0);
475 }
476
477 static void
478 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
479 {
480         int hlen, tlen, dlen;
481         struct tcphdr *tc;
482         char *data;
483         const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
484         const char *okstr = "OK", *client_port_str = "client_port";
485         const char *server_port_str = "server_port";
486         int i, parseOk;
487
488         (void)maxpacketsize;
489
490         tc = (struct tcphdr *)ip_next(pip);
491         hlen = (pip->ip_hl + tc->th_off) << 2;
492         tlen = ntohs(pip->ip_len);
493         dlen = tlen - hlen;
494
495         data = (char *)pip;
496         data += hlen;
497
498         /* When aliasing a client, check for the SETUP request */
499         if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
500             (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
501
502                 if (dlen >= (int)strlen(setup)) {
503                         if (memcmp(data, setup, strlen(setup)) == 0) {
504                                 alias_rtsp_out(la, pip, lnk, data, client_port_str);
505                                 return;
506                         }
507                 }
508                 if (dlen >= (int)strlen(pna)) {
509                         if (memcmp(data, pna, strlen(pna)) == 0) {
510                                 alias_pna_out(la, pip, lnk, data, dlen);
511                         }
512                 }
513         } else {
514
515                 /*
516                  * When aliasing a server, check for the 200 reply
517                  * Accomodate varying number of blanks between 200 & OK
518                  */
519
520                 if (dlen >= (int)strlen(str200)) {
521
522                         for (parseOk = 0, i = 0;
523                             i <= dlen - (int)strlen(str200);
524                             i++) {
525                                 if (memcmp(&data[i], str200, strlen(str200)) == 0) {
526                                         parseOk = 1;
527                                         break;
528                                 }
529                         }
530                         if (parseOk) {
531
532                                 i += strlen(str200);    /* skip string found */
533                                 while (data[i] == ' ')  /* skip blank(s) */
534                                         i++;
535
536                                 if ((dlen - i) >= (int)strlen(okstr)) {
537
538                                         if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
539                                                 alias_rtsp_out(la, pip, lnk, data, server_port_str);
540
541                                 }
542                         }
543                 }
544         }
545 }