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 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 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 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 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 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 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 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