1 /*-
2 * alias_smedia.c
3 *
4 * SPDX-License-Identifier: 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 /*
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
fingerprint(struct libalias * la,struct alias_data * ah)135 fingerprint(struct libalias *la, struct alias_data *ah)
136 {
137 if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL &&
138 ntohs(*ah->dport) == TFTP_PORT_NUMBER)
139 return (0);
140 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
141 ah->maxpktsize == 0)
142 return (-1);
143 if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1
144 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1
145 || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2
146 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2)
147 return (0);
148 return (-1);
149 }
150
151 static int
protohandler(struct libalias * la,struct ip * pip,struct alias_data * ah)152 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
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
mod_handler(module_t mod,int type,void * data)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
search_string(char * data,int dlen,const char * search_str)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 if (k == search_str_len - 1)
224 return (j + 1);
225 }
226 }
227 return (-1);
228 }
229
230 static int
alias_rtsp_out(struct libalias * la,struct ip * pip,struct alias_link * lnk,char * data,const char * port_str)231 alias_rtsp_out(struct libalias *la, struct ip *pip,
232 struct alias_link *lnk,
233 char *data,
234 const char *port_str)
235 {
236 int hlen, tlen, dlen;
237 struct tcphdr *tc;
238 int i, j, pos, state, port_dlen, new_dlen, delta;
239 u_short p[2], new_len;
240 u_short sport, eport, base_port;
241 u_short salias = 0, ealias = 0, base_alias = 0;
242 const char *transport_str = "transport:";
243 char newdata[2048], *port_data, *port_newdata, stemp[80];
244 int links_created = 0, pkt_updated = 0;
245 struct alias_link *rtsp_lnk = NULL;
246 struct in_addr null_addr;
247
248 /* Calculate data length of TCP packet */
249 tc = (struct tcphdr *)ip_next(pip);
250 hlen = (pip->ip_hl + tc->th_off) << 2;
251 tlen = ntohs(pip->ip_len);
252 dlen = tlen - hlen;
253
254 /* Find keyword, "Transport: " */
255 pos = search_string(data, dlen, transport_str);
256 if (pos < 0)
257 return (-1);
258
259 port_data = data + pos;
260 port_dlen = dlen - pos;
261
262 memcpy(newdata, data, pos);
263 port_newdata = newdata + pos;
264
265 while (port_dlen > (int)strlen(port_str)) {
266 /* Find keyword, appropriate port string */
267 pos = search_string(port_data, port_dlen, port_str);
268 if (pos < 0)
269 break;
270
271 memcpy(port_newdata, port_data, pos + 1);
272 port_newdata += (pos + 1);
273
274 p[0] = p[1] = 0;
275 sport = eport = 0;
276 state = 0;
277 for (i = pos; i < port_dlen; i++) {
278 switch (state) {
279 case 0:
280 if (port_data[i] == '=')
281 state++;
282 break;
283 case 1:
284 if (ISDIGIT(port_data[i]))
285 p[0] = p[0] * 10 + port_data[i] - '0';
286 else if (port_data[i] == ';')
287 state = 3;
288 else if (port_data[i] == '-')
289 state++;
290 break;
291 case 2:
292 if (ISDIGIT(port_data[i]))
293 p[1] = p[1] * 10 + port_data[i] - '0';
294 else
295 state++;
296 break;
297 case 3:
298 base_port = p[0];
299 sport = htons(p[0]);
300 eport = htons(p[1]);
301
302 if (!links_created) {
303 links_created = 1;
304 /*
305 * Find an even numbered port
306 * number base that satisfies the
307 * contiguous number of ports we
308 * need
309 */
310 null_addr.s_addr = 0;
311 if (0 == (salias = FindNewPortGroup(la, null_addr,
312 FindAliasAddress(la, pip->ip_src),
313 sport, 0,
314 RTSP_PORT_GROUP,
315 IPPROTO_UDP, 1))) {
316 #ifdef LIBALIAS_DEBUG
317 fprintf(stderr,
318 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
319 #endif
320 } else {
321 base_alias = ntohs(salias);
322 for (j = 0; j < RTSP_PORT_GROUP; j++) {
323 /*
324 * Establish link
325 * to port found in
326 * RTSP packet
327 */
328 rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
329 htons(base_port + j), htons(base_alias + j),
330 IPPROTO_UDP);
331 if (rtsp_lnk != NULL) {
332 #ifndef NO_FW_PUNCH
333 /*
334 * Punch
335 * hole in
336 * firewall
337 */
338 PunchFWHole(rtsp_lnk);
339 #endif
340 } else {
341 #ifdef LIBALIAS_DEBUG
342 fprintf(stderr,
343 "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
344 #endif
345 break;
346 }
347 }
348 }
349 ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
350 }
351 if (salias && rtsp_lnk) {
352 pkt_updated = 1;
353
354 /* Copy into IP packet */
355 sprintf(stemp, "%d", ntohs(salias));
356 memcpy(port_newdata, stemp, strlen(stemp));
357 port_newdata += strlen(stemp);
358
359 if (eport != 0) {
360 *port_newdata = '-';
361 port_newdata++;
362
363 /* Copy into IP packet */
364 sprintf(stemp, "%d", ntohs(ealias));
365 memcpy(port_newdata, stemp, strlen(stemp));
366 port_newdata += strlen(stemp);
367 }
368 *port_newdata = ';';
369 port_newdata++;
370 }
371 state++;
372 break;
373 }
374 if (state > 3) {
375 break;
376 }
377 }
378 port_data += i;
379 port_dlen -= i;
380 }
381
382 if (!pkt_updated)
383 return (-1);
384
385 memcpy(port_newdata, port_data, port_dlen);
386 port_newdata += port_dlen;
387 *port_newdata = '\0';
388
389 /* Create new packet */
390 new_dlen = port_newdata - newdata;
391 memcpy(data, newdata, new_dlen);
392
393 SetAckModified(lnk);
394 tc = (struct tcphdr *)ip_next(pip);
395 delta = GetDeltaSeqOut(tc->th_seq, lnk);
396 AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len,
397 tc->th_seq, tc->th_off);
398
399 new_len = htons(hlen + new_dlen);
400 DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1);
401 pip->ip_len = new_len;
402
403 tc->th_sum = 0;
404 #ifdef _KERNEL
405 tc->th_x2 = (TH_RES1 >> 8);
406 #else
407 tc->th_sum = TcpChecksum(pip);
408 #endif
409 return (0);
410 }
411
412 /* Support the protocol used by early versions of RealPlayer */
413
414 static int
alias_pna_out(struct libalias * la,struct ip * pip,struct alias_link * lnk,char * data,int dlen)415 alias_pna_out(struct libalias *la, struct ip *pip,
416 struct alias_link *lnk,
417 char *data,
418 int dlen)
419 {
420 struct alias_link *pna_links;
421 u_short msg_id, msg_len;
422 char *work;
423 u_short alias_port, port;
424 struct tcphdr *tc;
425
426 work = data;
427 work += 5;
428 while (work + 4 < data + dlen) {
429 memcpy(&msg_id, work, 2);
430 work += 2;
431 memcpy(&msg_len, work, 2);
432 work += 2;
433 if (ntohs(msg_id) == 0) /* end of options */
434 return (0);
435
436 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
437 memcpy(&port, work, 2);
438 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
439 port, 0, IPPROTO_UDP, 1);
440 if (pna_links != NULL) {
441 #ifndef NO_FW_PUNCH
442 /* Punch hole in firewall */
443 PunchFWHole(pna_links);
444 #endif
445 tc = (struct tcphdr *)ip_next(pip);
446 alias_port = GetAliasPort(pna_links);
447 memcpy(work, &alias_port, 2);
448
449 /* Compute TCP checksum for revised packet */
450 tc->th_sum = 0;
451 #ifdef _KERNEL
452 tc->th_x2 = (TH_RES1 >> 8);
453 #else
454 tc->th_sum = TcpChecksum(pip);
455 #endif
456 }
457 }
458 work += ntohs(msg_len);
459 }
460
461 return (0);
462 }
463
464 static void
AliasHandleRtspOut(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize)465 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
466 {
467 int hlen, tlen, dlen;
468 struct tcphdr *tc;
469 char *data;
470 const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
471 const char *okstr = "OK", *client_port_str = "client_port";
472 const char *server_port_str = "server_port";
473 int i, parseOk;
474
475 (void)maxpacketsize;
476
477 tc = (struct tcphdr *)ip_next(pip);
478 hlen = (pip->ip_hl + tc->th_off) << 2;
479 tlen = ntohs(pip->ip_len);
480 dlen = tlen - hlen;
481
482 data = (char *)pip;
483 data += hlen;
484
485 /* When aliasing a client, check for the SETUP request */
486 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
487 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
488 if (dlen >= (int)strlen(setup) &&
489 memcmp(data, setup, strlen(setup)) == 0) {
490 alias_rtsp_out(la, pip, lnk, data, client_port_str);
491 return;
492 }
493
494 if (dlen >= (int)strlen(pna) &&
495 memcmp(data, pna, strlen(pna)) == 0)
496 alias_pna_out(la, pip, lnk, data, dlen);
497 } else {
498 /*
499 * When aliasing a server, check for the 200 reply
500 * Accommodate varying number of blanks between 200 & OK
501 */
502
503 if (dlen >= (int)strlen(str200)) {
504 for (parseOk = 0, i = 0;
505 i <= dlen - (int)strlen(str200);
506 i++)
507 if (memcmp(&data[i], str200, strlen(str200)) == 0) {
508 parseOk = 1;
509 break;
510 }
511
512 if (parseOk) {
513 i += strlen(str200); /* skip string found */
514 while (data[i] == ' ') /* skip blank(s) */
515 i++;
516
517 if ((dlen - i) >= (int)strlen(okstr))
518 if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
519 alias_rtsp_out(la, pip, lnk, data, server_port_str);
520 }
521 }
522 }
523 }
524