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