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 <sys/types.h>
108 #include <errno.h>
109 #include <stdbool.h>
110 #include <stdio.h>
111 #include <string.h>
112 #endif
113
114 #include <netinet/in_systm.h>
115 #include <netinet/in.h>
116 #include <netinet/ip.h>
117 #include <netinet/tcp.h>
118
119 #ifdef _KERNEL
120 #include <netinet/libalias/alias.h>
121 #include <netinet/libalias/alias_local.h>
122 #include <netinet/libalias/alias_mod.h>
123 #else
124 #include "alias_local.h"
125 #include "alias_mod.h"
126 #endif
127
128 #define RTSP_CONTROL_PORT_NUMBER_1 554
129 #define RTSP_CONTROL_PORT_NUMBER_2 7070
130 #define TFTP_PORT_NUMBER 69
131
132 static void AliasHandleRtspOut(struct libalias *, struct ip *,
133 struct alias_link *, size_t);
134
135 static int
fingerprint(struct libalias * la,struct alias_data * ah)136 fingerprint(struct libalias *la, struct alias_data *ah)
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
protohandler(struct libalias * la,struct ip * pip,struct alias_data * ah)153 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
154 {
155 if (ntohs(*ah->dport) == TFTP_PORT_NUMBER)
156 FindRtspOut(la, pip->ip_src, pip->ip_dst,
157 *ah->sport, *ah->aport, IPPROTO_UDP);
158 else
159 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
mod_handler(module_t mod,int type,void * data)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 ssize_t
search_string(char * data,size_t dlen,const char * search_str)214 search_string(char *data, size_t dlen, const char *search_str)
215 {
216 size_t i, j, k;
217 size_t search_str_len;
218
219 search_str_len = strlen(search_str);
220 if (search_str_len > dlen)
221 return (-1);
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 if (k == search_str_len - 1)
228 return (j + 1);
229 }
230 }
231 return (-1);
232 }
233
234 static int
alias_rtsp_out(struct libalias * la,struct ip * pip,struct alias_link * lnk,char * data,size_t maxlen,const char * port_str)235 alias_rtsp_out(struct libalias *la, struct ip *pip,
236 struct alias_link *lnk,
237 char *data, size_t maxlen,
238 const char *port_str)
239 {
240 size_t hlen, tlen, dlen;
241 size_t i, j;
242 struct tcphdr *tc;
243 int delta, state;
244 ssize_t pos, slen;
245 size_t new_dlen, port_dlen, port_slen;
246 u_short p[2], new_len;
247 u_short sport, eport, base_port;
248 u_short salias = 0, ealias = 0, base_alias = 0;
249 const char *transport_str = "transport:";
250 char *newdata, *port_data;
251 bool links_created = false, pkt_updated = false;
252 struct alias_link *rtsp_lnk = NULL;
253 struct in_addr null_addr;
254
255 /* Calculate data length of TCP packet */
256 tc = (struct tcphdr *)ip_next(pip);
257 hlen = (pip->ip_hl + tc->th_off) << 2;
258 tlen = ntohs(pip->ip_len);
259 dlen = tlen - hlen;
260 if (hlen > tlen || tlen > maxlen)
261 return (-1);
262 port_slen = strlen(port_str);
263
264 /* Find keyword, "Transport: " */
265 pos = search_string(data, dlen, transport_str);
266 if (pos < 0)
267 return (-1);
268
269 port_data = data + pos;
270 port_dlen = dlen - pos;
271
272 /* Allocate temporary buffer */
273 maxlen -= hlen;
274 if ((newdata = malloc(maxlen)) == NULL)
275 return (-1);
276 memcpy(newdata, data, pos);
277 new_dlen = pos;
278
279 while (port_dlen > port_slen) {
280 /* Find keyword, appropriate port string */
281 pos = search_string(port_data, port_dlen, port_str);
282 if (pos < 0 || (size_t)pos + 1 > maxlen - new_dlen)
283 break;
284
285 memcpy(newdata + new_dlen, port_data, pos + 1);
286 new_dlen += pos + 1;
287
288 p[0] = p[1] = 0;
289 sport = eport = 0;
290 state = 0;
291 for (i = pos; i < port_dlen; i++) {
292 switch (state) {
293 case 0:
294 if (port_data[i] == '=')
295 state++;
296 break;
297 case 1:
298 if (ISDIGIT(port_data[i]))
299 p[0] = p[0] * 10 + port_data[i] - '0';
300 else if (port_data[i] == ';')
301 state = 3;
302 else if (port_data[i] == '-')
303 state++;
304 break;
305 case 2:
306 if (ISDIGIT(port_data[i]))
307 p[1] = p[1] * 10 + port_data[i] - '0';
308 else
309 state++;
310 break;
311 case 3:
312 base_port = p[0];
313 sport = htons(p[0]);
314 eport = htons(p[1]);
315
316 if (!links_created) {
317 links_created = true;
318 /*
319 * Find an even numbered port
320 * number base that satisfies the
321 * contiguous number of ports we
322 * need
323 */
324 null_addr.s_addr = 0;
325 if (0 == (salias = FindNewPortGroup(la, null_addr,
326 FindAliasAddress(la, pip->ip_src),
327 sport, 0,
328 RTSP_PORT_GROUP,
329 IPPROTO_UDP, 1))) {
330 #ifdef LIBALIAS_DEBUG
331 fprintf(stderr,
332 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
333 #endif
334 } else {
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 pkt_updated = true;
367
368 /* Copy into IP packet */
369 slen = snprintf(newdata + new_dlen,
370 maxlen - new_dlen, "%d", ntohs(salias));
371 if (slen < 0 || slen >= maxlen - new_dlen)
372 goto fail;
373 new_dlen += slen;
374
375 if (eport != 0) {
376 if (new_dlen == maxlen)
377 goto fail;
378 newdata[new_dlen++] = '-';
379
380 /* Copy into IP packet */
381 slen = snprintf(newdata + new_dlen,
382 maxlen - new_dlen, "%d", ntohs(ealias));
383 if (slen < 0 || slen >= maxlen - new_dlen)
384 goto fail;
385 new_dlen += slen;
386 }
387 if (new_dlen == maxlen)
388 goto fail;
389 newdata[new_dlen++] = ';';
390 }
391 state++;
392 break;
393 }
394 if (state > 3) {
395 break;
396 }
397 }
398 port_data += i;
399 port_dlen -= i;
400 }
401
402 if (!pkt_updated)
403 goto fail;
404
405 /* Create new packet */
406 if (new_dlen + port_dlen > maxlen)
407 goto fail;
408 memmove(data + new_dlen, port_data, port_dlen);
409 memcpy(data, newdata, new_dlen);
410 new_dlen += port_dlen;
411 free(newdata);
412
413 SetAckModified(lnk);
414 tc = (struct tcphdr *)ip_next(pip);
415 delta = GetDeltaSeqOut(tc->th_seq, lnk);
416 AddSeq(lnk, delta + (int)(new_dlen - dlen), pip->ip_hl, pip->ip_len,
417 tc->th_seq, tc->th_off);
418
419 new_len = htons(hlen + new_dlen);
420 DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1);
421 pip->ip_len = new_len;
422
423 tc->th_sum = 0;
424 #ifdef _KERNEL
425 tcp_set_flags(tc, tcp_get_flags(tc) | TH_RES1);
426 #else
427 tc->th_sum = TcpChecksum(pip);
428 #endif
429 return (0);
430 fail:
431 free(newdata);
432 return (-1);
433 }
434
435 /* Support the protocol used by early versions of RealPlayer */
436
437 static int
alias_pna_out(struct libalias * la,struct ip * pip,struct alias_link * lnk,char * data,size_t dlen)438 alias_pna_out(struct libalias *la, struct ip *pip,
439 struct alias_link *lnk,
440 char *data,
441 size_t dlen)
442 {
443 struct alias_link *pna_links;
444 char *work;
445 struct tcphdr *tc;
446 u_short msg_id, msg_len;
447 u_short alias_port, port;
448
449 work = data;
450 work += 5;
451 while (work + 4 < data + dlen) {
452 memcpy(&msg_id, work, 2);
453 work += 2;
454 memcpy(&msg_len, work, 2);
455 work += 2;
456 if (ntohs(msg_id) == 0) /* end of options */
457 return (0);
458
459 if (ntohs(msg_id) == 1 || ntohs(msg_id) == 7) {
460 memcpy(&port, work, 2);
461 (void)FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
462 port, 0, IPPROTO_UDP, 1, &pna_links);
463 if (pna_links != NULL) {
464 #ifndef NO_FW_PUNCH
465 /* Punch hole in firewall */
466 PunchFWHole(pna_links);
467 #endif
468 tc = (struct tcphdr *)ip_next(pip);
469 alias_port = GetAliasPort(pna_links);
470 memcpy(work, &alias_port, 2);
471
472 /* Compute TCP checksum for revised packet */
473 tc->th_sum = 0;
474 #ifdef _KERNEL
475 tcp_set_flags(tc, tcp_get_flags(tc) | TH_RES1);
476 #else
477 tc->th_sum = TcpChecksum(pip);
478 #endif
479 }
480 }
481 work += ntohs(msg_len);
482 }
483
484 return (0);
485 }
486
487 static void
AliasHandleRtspOut(struct libalias * la,struct ip * pip,struct alias_link * lnk,size_t maxlen)488 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk,
489 size_t maxlen)
490 {
491 struct tcphdr *tc;
492 char *data;
493 const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
494 const char *okstr = "OK", *client_port_str = "client_port";
495 const char *server_port_str = "server_port";
496 size_t hlen, tlen, dlen;
497 size_t i;
498
499 tc = (struct tcphdr *)ip_next(pip);
500 hlen = (pip->ip_hl + tc->th_off) << 2;
501 tlen = ntohs(pip->ip_len);
502 dlen = tlen - hlen;
503 if (hlen > tlen || tlen > maxlen)
504 return;
505
506 data = (char *)pip;
507 data += hlen;
508
509 /* When aliasing a client, check for the SETUP request */
510 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
511 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
512 if (dlen >= strlen(setup) &&
513 memcmp(data, setup, strlen(setup)) == 0) {
514 alias_rtsp_out(la, pip, lnk, data, maxlen,
515 client_port_str);
516 return;
517 }
518
519 if (dlen >= strlen(pna) &&
520 memcmp(data, pna, strlen(pna)) == 0)
521 alias_pna_out(la, pip, lnk, data, dlen);
522 } else {
523 /*
524 * When aliasing a server, check for the 200 reply
525 * Accommodate varying number of blanks between 200 & OK
526 */
527 if (dlen < strlen(str200) + 1 + strlen(okstr))
528 return;
529 for (i = 0; i <= dlen - strlen(str200) - 1; i++) {
530 if (memcmp(&data[i], str200, strlen(str200)) == 0 &&
531 data[i + strlen(str200)] == ' ') {
532 i += strlen(str200); /* skip 200 */
533 while (i < dlen && data[i] == ' ') /* skip blank(s) */
534 i++;
535 if (dlen - i >= strlen(okstr)) {
536 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) {
537 alias_rtsp_out(la, pip, lnk, data,
538 maxlen, server_port_str);
539 break;
540 }
541 }
542 }
543 }
544 }
545 }
546