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