]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/libalias/alias_smedia.c
MFV 364468:
[FreeBSD/FreeBSD.git] / sys / netinet / libalias / alias_smedia.c
1 /*-
2  * alias_smedia.c
3  *
4  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause
5  *
6  * Copyright (c) 2000 Whistle Communications, Inc.
7  * All rights reserved.
8  *
9  * Subject to the following obligations and disclaimer of warranty, use and
10  * redistribution of this software, in source or object code forms, with or
11  * without modifications are expressly permitted by Whistle Communications;
12  * provided, however, that:
13  * 1. Any and all reproductions of the source or object code must include the
14  *    copyright notice above and the following disclaimer of warranties; and
15  * 2. No rights are granted, in any manner or form, to use Whistle
16  *    Communications, Inc. trademarks, including the mark "WHISTLE
17  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18  *    such appears in the above copyright notice or in the software.
19  *
20  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  *
38  * Copyright (c) 2000  Junichi SATOH <junichi@astec.co.jp>
39  *                                   <junichi@junichi.org>
40  * All rights reserved.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  *
63  * Authors: Erik Salander <erik@whistle.com>
64  *          Junichi SATOH <junichi@astec.co.jp>
65  *                        <junichi@junichi.org>
66  */
67
68 #include <sys/cdefs.h>
69 __FBSDID("$FreeBSD$");
70
71 /*
72    Alias_smedia.c is meant to contain the aliasing code for streaming media
73    protocols.  It performs special processing for RSTP sessions under TCP.
74    Specifically, when a SETUP request is sent by a client, or a 200 reply
75    is sent by a server, it is intercepted and modified.  The address is
76    changed to the gateway machine and an aliasing port is used.
77
78    More specifically, the "client_port" configuration parameter is
79    parsed for SETUP requests.  The "server_port" configuration parameter is
80    parsed for 200 replies eminating from a server.  This is intended to handle
81    the unicast case.
82
83    RTSP also allows a redirection of a stream to another client by using the
84    "destination" configuration parameter.  The destination config parm would
85    indicate a different IP address.  This function is NOT supported by the
86    RTSP translation code below.
87
88    The RTSP multicast functions without any address translation intervention.
89
90    For this routine to work, the SETUP/200 must fit entirely
91    into a single TCP packet.  This is typically the case, but exceptions
92    can easily be envisioned under the actual specifications.
93
94    Probably the most troubling aspect of the approach taken here is
95    that the new SETUP/200 will typically be a different length, and
96    this causes a certain amount of bookkeeping to keep track of the
97    changes of sequence and acknowledgment numbers, since the client
98    machine is totally unaware of the modification to the TCP stream.
99
100    Initial version:  May, 2000 (eds)
101 */
102
103 #ifdef _KERNEL
104 #include <sys/param.h>
105 #include <sys/systm.h>
106 #include <sys/kernel.h>
107 #include <sys/module.h>
108 #else
109 #include <errno.h>
110 #include <sys/types.h>
111 #include <stdio.h>
112 #include <string.h>
113 #endif
114
115 #include <netinet/in_systm.h>
116 #include <netinet/in.h>
117 #include <netinet/ip.h>
118 #include <netinet/tcp.h>
119
120 #ifdef _KERNEL
121 #include <netinet/libalias/alias.h>
122 #include <netinet/libalias/alias_local.h>
123 #include <netinet/libalias/alias_mod.h>
124 #else
125 #include "alias_local.h"
126 #include "alias_mod.h"
127 #endif
128
129 #define RTSP_CONTROL_PORT_NUMBER_1 554
130 #define RTSP_CONTROL_PORT_NUMBER_2 7070
131 #define TFTP_PORT_NUMBER 69
132
133 static void
134 AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, 
135                   int maxpacketsize);
136 static int
137 fingerprint(struct libalias *la, struct alias_data *ah)
138 {
139
140         if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL &&
141             ntohs(*ah->dport) == TFTP_PORT_NUMBER)
142                 return (0);
143         if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
144             ah->maxpktsize == 0)
145                 return (-1);
146         if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1
147             || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1
148             || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2
149             || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2)
150                 return (0);
151         return (-1);
152 }
153
154 static int
155 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
156 {
157         
158         if (ntohs(*ah->dport) == TFTP_PORT_NUMBER)
159                 FindRtspOut(la, pip->ip_src, pip->ip_dst,
160                             *ah->sport, *ah->aport, IPPROTO_UDP);
161         else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize);      
162         return (0);
163 }
164
165 struct proto_handler handlers[] = {
166         {
167           .pri = 100,
168           .dir = OUT,
169           .proto = TCP|UDP,
170           .fingerprint = &fingerprint,
171           .protohandler = &protohandler
172         },
173         { EOH }
174 };
175
176 static int
177 mod_handler(module_t mod, int type, void *data)
178 {
179         int error;
180
181         switch (type) {
182         case MOD_LOAD:
183                 error = 0;
184                 LibAliasAttachHandlers(handlers);
185                 break;
186         case MOD_UNLOAD:
187                 error = 0;
188                 LibAliasDetachHandlers(handlers);
189                 break;
190         default:
191                 error = EINVAL;
192         }
193         return (error);
194 }
195
196 #ifdef _KERNEL
197 static
198 #endif
199 moduledata_t alias_mod = {
200        "alias_smedia", mod_handler, NULL
201 };
202
203 #ifdef  _KERNEL
204 DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
205 MODULE_VERSION(alias_smedia, 1);
206 MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1);
207 #endif
208
209 #define RTSP_CONTROL_PORT_NUMBER_1 554
210 #define RTSP_CONTROL_PORT_NUMBER_2 7070
211 #define RTSP_PORT_GROUP            2
212
213 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
214
215 static int
216 search_string(char *data, int dlen, const char *search_str)
217 {
218         int i, j, k;
219         int search_str_len;
220
221         search_str_len = strlen(search_str);
222         for (i = 0; i < dlen - search_str_len; i++) {
223                 for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
224                         if (data[j] != search_str[k] &&
225                             data[j] != search_str[k] - ('a' - 'A')) {
226                                 break;
227                         }
228                         if (k == search_str_len - 1) {
229                                 return (j + 1);
230                         }
231                 }
232         }
233         return (-1);
234 }
235
236 static int
237 alias_rtsp_out(struct libalias *la, struct ip *pip,
238     struct alias_link *lnk,
239     char *data,
240     const char *port_str)
241 {
242         int hlen, tlen, dlen;
243         struct tcphdr *tc;
244         int i, j, pos, state, port_dlen, new_dlen, delta;
245         u_short p[2], new_len;
246         u_short sport, eport, base_port;
247         u_short salias = 0, ealias = 0, base_alias = 0;
248         const char *transport_str = "transport:";
249         char newdata[2048], *port_data, *port_newdata, stemp[80];
250         int links_created = 0, pkt_updated = 0;
251         struct alias_link *rtsp_lnk = NULL;
252         struct in_addr null_addr;
253
254         /* Calculate data length of TCP packet */
255         tc = (struct tcphdr *)ip_next(pip);
256         hlen = (pip->ip_hl + tc->th_off) << 2;
257         tlen = ntohs(pip->ip_len);
258         dlen = tlen - hlen;
259
260         /* Find keyword, "Transport: " */
261         pos = search_string(data, dlen, transport_str);
262         if (pos < 0) {
263                 return (-1);
264         }
265         port_data = data + pos;
266         port_dlen = dlen - pos;
267
268         memcpy(newdata, data, pos);
269         port_newdata = newdata + pos;
270
271         while (port_dlen > (int)strlen(port_str)) {
272                 /* Find keyword, appropriate port string */
273                 pos = search_string(port_data, port_dlen, port_str);
274                 if (pos < 0) {
275                         break;
276                 }
277                 memcpy(port_newdata, port_data, pos + 1);
278                 port_newdata += (pos + 1);
279
280                 p[0] = p[1] = 0;
281                 sport = eport = 0;
282                 state = 0;
283                 for (i = pos; i < port_dlen; i++) {
284                         switch (state) {
285                         case 0:
286                                 if (port_data[i] == '=') {
287                                         state++;
288                                 }
289                                 break;
290                         case 1:
291                                 if (ISDIGIT(port_data[i])) {
292                                         p[0] = p[0] * 10 + port_data[i] - '0';
293                                 } else {
294                                         if (port_data[i] == ';') {
295                                                 state = 3;
296                                         }
297                                         if (port_data[i] == '-') {
298                                                 state++;
299                                         }
300                                 }
301                                 break;
302                         case 2:
303                                 if (ISDIGIT(port_data[i])) {
304                                         p[1] = p[1] * 10 + port_data[i] - '0';
305                                 } else {
306                                         state++;
307                                 }
308                                 break;
309                         case 3:
310                                 base_port = p[0];
311                                 sport = htons(p[0]);
312                                 eport = htons(p[1]);
313
314                                 if (!links_created) {
315
316                                         links_created = 1;
317                                         /*
318                                          * Find an even numbered port
319                                          * number base that satisfies the
320                                          * contiguous number of ports we
321                                          * need
322                                          */
323                                         null_addr.s_addr = 0;
324                                         if (0 == (salias = FindNewPortGroup(la, null_addr,
325                                             FindAliasAddress(la, pip->ip_src),
326                                             sport, 0,
327                                             RTSP_PORT_GROUP,
328                                             IPPROTO_UDP, 1))) {
329 #ifdef LIBALIAS_DEBUG
330                                                 fprintf(stderr,
331                                                     "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
332 #endif
333                                         } else {
334
335                                                 base_alias = ntohs(salias);
336                                                 for (j = 0; j < RTSP_PORT_GROUP; j++) {
337                                                         /*
338                                                          * Establish link
339                                                          * to port found in
340                                                          * RTSP packet
341                                                          */
342                                                         rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
343                                                             htons(base_port + j), htons(base_alias + j),
344                                                             IPPROTO_UDP);
345                                                         if (rtsp_lnk != NULL) {
346 #ifndef NO_FW_PUNCH
347                                                                 /*
348                                                                  * Punch
349                                                                  * hole in
350                                                                  * firewall
351                                                                  */
352                                                                 PunchFWHole(rtsp_lnk);
353 #endif
354                                                         } else {
355 #ifdef LIBALIAS_DEBUG
356                                                                 fprintf(stderr,
357                                                                     "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
358 #endif
359                                                                 break;
360                                                         }
361                                                 }
362                                         }
363                                         ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
364                                 }
365                                 if (salias && rtsp_lnk) {
366
367                                         pkt_updated = 1;
368
369                                         /* Copy into IP packet */
370                                         sprintf(stemp, "%d", ntohs(salias));
371                                         memcpy(port_newdata, stemp, strlen(stemp));
372                                         port_newdata += strlen(stemp);
373
374                                         if (eport != 0) {
375                                                 *port_newdata = '-';
376                                                 port_newdata++;
377
378                                                 /* Copy into IP packet */
379                                                 sprintf(stemp, "%d", ntohs(ealias));
380                                                 memcpy(port_newdata, stemp, strlen(stemp));
381                                                 port_newdata += strlen(stemp);
382                                         }
383                                         *port_newdata = ';';
384                                         port_newdata++;
385                                 }
386                                 state++;
387                                 break;
388                         }
389                         if (state > 3) {
390                                 break;
391                         }
392                 }
393                 port_data += i;
394                 port_dlen -= i;
395         }
396
397         if (!pkt_updated)
398                 return (-1);
399
400         memcpy(port_newdata, port_data, port_dlen);
401         port_newdata += port_dlen;
402         *port_newdata = '\0';
403
404         /* Create new packet */
405         new_dlen = port_newdata - newdata;
406         memcpy(data, newdata, new_dlen);
407
408         SetAckModified(lnk);
409         tc = (struct tcphdr *)ip_next(pip);
410         delta = GetDeltaSeqOut(tc->th_seq, lnk);
411         AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len,
412             tc->th_seq, tc->th_off);
413
414         new_len = htons(hlen + new_dlen);
415         DifferentialChecksum(&pip->ip_sum,
416             &new_len,
417             &pip->ip_len,
418             1);
419         pip->ip_len = new_len;
420
421         tc->th_sum = 0;
422 #ifdef _KERNEL
423         tc->th_x2 = 1;
424 #else
425         tc->th_sum = TcpChecksum(pip);
426 #endif
427         return (0);
428 }
429
430 /* Support the protocol used by early versions of RealPlayer */
431
432 static int
433 alias_pna_out(struct libalias *la, struct ip *pip,
434     struct alias_link *lnk,
435     char *data,
436     int dlen)
437 {
438         struct alias_link *pna_links;
439         u_short msg_id, msg_len;
440         char *work;
441         u_short alias_port, port;
442         struct tcphdr *tc;
443
444         work = data;
445         work += 5;
446         while (work + 4 < data + dlen) {
447                 memcpy(&msg_id, work, 2);
448                 work += 2;
449                 memcpy(&msg_len, work, 2);
450                 work += 2;
451                 if (ntohs(msg_id) == 0) {
452                         /* end of options */
453                         return (0);
454                 }
455                 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
456                         memcpy(&port, work, 2);
457                         pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
458                             port, 0, IPPROTO_UDP, 1);
459                         if (pna_links != NULL) {
460 #ifndef NO_FW_PUNCH
461                                 /* Punch hole in firewall */
462                                 PunchFWHole(pna_links);
463 #endif
464                                 tc = (struct tcphdr *)ip_next(pip);
465                                 alias_port = GetAliasPort(pna_links);
466                                 memcpy(work, &alias_port, 2);
467
468                                 /* Compute TCP checksum for revised packet */
469                                 tc->th_sum = 0;
470 #ifdef _KERNEL
471                                 tc->th_x2 = 1;
472 #else
473                                 tc->th_sum = TcpChecksum(pip);
474 #endif
475                         }
476                 }
477                 work += ntohs(msg_len);
478         }
479
480         return (0);
481 }
482
483 static void
484 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
485 {
486         int hlen, tlen, dlen;
487         struct tcphdr *tc;
488         char *data;
489         const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
490         const char *okstr = "OK", *client_port_str = "client_port";
491         const char *server_port_str = "server_port";
492         int i, parseOk;
493
494         (void)maxpacketsize;
495
496         tc = (struct tcphdr *)ip_next(pip);
497         hlen = (pip->ip_hl + tc->th_off) << 2;
498         tlen = ntohs(pip->ip_len);
499         dlen = tlen - hlen;
500
501         data = (char *)pip;
502         data += hlen;
503
504         /* When aliasing a client, check for the SETUP request */
505         if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
506             (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
507
508                 if (dlen >= (int)strlen(setup)) {
509                         if (memcmp(data, setup, strlen(setup)) == 0) {
510                                 alias_rtsp_out(la, pip, lnk, data, client_port_str);
511                                 return;
512                         }
513                 }
514                 if (dlen >= (int)strlen(pna)) {
515                         if (memcmp(data, pna, strlen(pna)) == 0) {
516                                 alias_pna_out(la, pip, lnk, data, dlen);
517                         }
518                 }
519         } else {
520
521                 /*
522                  * When aliasing a server, check for the 200 reply
523                  * Accommodate varying number of blanks between 200 & OK
524                  */
525
526                 if (dlen >= (int)strlen(str200)) {
527
528                         for (parseOk = 0, i = 0;
529                             i <= dlen - (int)strlen(str200);
530                             i++) {
531                                 if (memcmp(&data[i], str200, strlen(str200)) == 0) {
532                                         parseOk = 1;
533                                         break;
534                                 }
535                         }
536                         if (parseOk) {
537
538                                 i += strlen(str200);    /* skip string found */
539                                 while (data[i] == ' ')  /* skip blank(s) */
540                                         i++;
541
542                                 if ((dlen - i) >= (int)strlen(okstr)) {
543
544                                         if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
545                                                 alias_rtsp_out(la, pip, lnk, data, server_port_str);
546
547                                 }
548                         }
549                 }
550         }
551 }