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 <[email protected]>
39 * <[email protected]>
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 <[email protected]>
64 * Junichi SATOH <[email protected]>
65 * <[email protected]>
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
fingerprint(struct libalias * la,struct alias_data * ah)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
protohandler(struct libalias * la,struct ip * pip,struct alias_data * ah)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
mod_handler(module_t mod,int type,void * data)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
search_string(char * data,int dlen,const char * search_str)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
alias_rtsp_out(struct libalias * la,struct ip * pip,struct alias_link * lnk,char * data,const char * port_str)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 links_created = 1;
316 /*
317 * Find an even numbered port
318 * number base that satisfies the
319 * contiguous number of ports we
320 * need
321 */
322 null_addr.s_addr = 0;
323 if (0 == (salias = FindNewPortGroup(la, null_addr,
324 FindAliasAddress(la, pip->ip_src),
325 sport, 0,
326 RTSP_PORT_GROUP,
327 IPPROTO_UDP, 1))) {
328 #ifdef LIBALIAS_DEBUG
329 fprintf(stderr,
330 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
331 #endif
332 } else {
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 pkt_updated = 1;
365
366 /* Copy into IP packet */
367 sprintf(stemp, "%d", ntohs(salias));
368 memcpy(port_newdata, stemp, strlen(stemp));
369 port_newdata += strlen(stemp);
370
371 if (eport != 0) {
372 *port_newdata = '-';
373 port_newdata++;
374
375 /* Copy into IP packet */
376 sprintf(stemp, "%d", ntohs(ealias));
377 memcpy(port_newdata, stemp, strlen(stemp));
378 port_newdata += strlen(stemp);
379 }
380 *port_newdata = ';';
381 port_newdata++;
382 }
383 state++;
384 break;
385 }
386 if (state > 3) {
387 break;
388 }
389 }
390 port_data += i;
391 port_dlen -= i;
392 }
393
394 if (!pkt_updated)
395 return (-1);
396
397 memcpy(port_newdata, port_data, port_dlen);
398 port_newdata += port_dlen;
399 *port_newdata = '\0';
400
401 /* Create new packet */
402 new_dlen = port_newdata - newdata;
403 memcpy(data, newdata, new_dlen);
404
405 SetAckModified(lnk);
406 tc = (struct tcphdr *)ip_next(pip);
407 delta = GetDeltaSeqOut(tc->th_seq, lnk);
408 AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len,
409 tc->th_seq, tc->th_off);
410
411 new_len = htons(hlen + new_dlen);
412 DifferentialChecksum(&pip->ip_sum,
413 &new_len,
414 &pip->ip_len,
415 1);
416 pip->ip_len = new_len;
417
418 tc->th_sum = 0;
419 #ifdef _KERNEL
420 tc->th_x2 = 1;
421 #else
422 tc->th_sum = TcpChecksum(pip);
423 #endif
424 return (0);
425 }
426
427 /* Support the protocol used by early versions of RealPlayer */
428
429 static int
alias_pna_out(struct libalias * la,struct ip * pip,struct alias_link * lnk,char * data,int dlen)430 alias_pna_out(struct libalias *la, struct ip *pip,
431 struct alias_link *lnk,
432 char *data,
433 int dlen)
434 {
435 struct alias_link *pna_links;
436 u_short msg_id, msg_len;
437 char *work;
438 u_short alias_port, port;
439 struct tcphdr *tc;
440
441 work = data;
442 work += 5;
443 while (work + 4 < data + dlen) {
444 memcpy(&msg_id, work, 2);
445 work += 2;
446 memcpy(&msg_len, work, 2);
447 work += 2;
448 if (ntohs(msg_id) == 0) {
449 /* end of options */
450 return (0);
451 }
452 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
453 memcpy(&port, work, 2);
454 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
455 port, 0, IPPROTO_UDP, 1);
456 if (pna_links != NULL) {
457 #ifndef NO_FW_PUNCH
458 /* Punch hole in firewall */
459 PunchFWHole(pna_links);
460 #endif
461 tc = (struct tcphdr *)ip_next(pip);
462 alias_port = GetAliasPort(pna_links);
463 memcpy(work, &alias_port, 2);
464
465 /* Compute TCP checksum for revised packet */
466 tc->th_sum = 0;
467 #ifdef _KERNEL
468 tc->th_x2 = 1;
469 #else
470 tc->th_sum = TcpChecksum(pip);
471 #endif
472 }
473 }
474 work += ntohs(msg_len);
475 }
476
477 return (0);
478 }
479
480 static void
AliasHandleRtspOut(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize)481 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
482 {
483 int hlen, tlen, dlen;
484 struct tcphdr *tc;
485 char *data;
486 const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
487 const char *okstr = "OK", *client_port_str = "client_port";
488 const char *server_port_str = "server_port";
489 int i, parseOk;
490
491 (void)maxpacketsize;
492
493 tc = (struct tcphdr *)ip_next(pip);
494 hlen = (pip->ip_hl + tc->th_off) << 2;
495 tlen = ntohs(pip->ip_len);
496 dlen = tlen - hlen;
497
498 data = (char *)pip;
499 data += hlen;
500
501 /* When aliasing a client, check for the SETUP request */
502 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
503 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
504 if (dlen >= (int)strlen(setup)) {
505 if (memcmp(data, setup, strlen(setup)) == 0) {
506 alias_rtsp_out(la, pip, lnk, data, client_port_str);
507 return;
508 }
509 }
510 if (dlen >= (int)strlen(pna)) {
511 if (memcmp(data, pna, strlen(pna)) == 0) {
512 alias_pna_out(la, pip, lnk, data, dlen);
513 }
514 }
515 } else {
516 /*
517 * When aliasing a server, check for the 200 reply
518 * Accommodate varying number of blanks between 200 & OK
519 */
520
521 if (dlen >= (int)strlen(str200)) {
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 i += strlen(str200); /* skip string found */
532 while (data[i] == ' ') /* skip blank(s) */
533 i++;
534
535 if ((dlen - i) >= (int)strlen(okstr)) {
536 if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
537 alias_rtsp_out(la, pip, lnk, data, server_port_str);
538 }
539 }
540 }
541 }
542 }
543