11573Srgrimes/* 21573Srgrimes * alias_smedia.c 31573Srgrimes * 41573Srgrimes * Copyright (c) 2000 Whistle Communications, Inc. 51573Srgrimes * All rights reserved. 61573Srgrimes * 71573Srgrimes * Subject to the following obligations and disclaimer of warranty, use and 81573Srgrimes * redistribution of this software, in source or object code forms, with or 91573Srgrimes * without modifications are expressly permitted by Whistle Communications; 101573Srgrimes * provided, however, that: 111573Srgrimes * 1. Any and all reproductions of the source or object code must include the 121573Srgrimes * copyright notice above and the following disclaimer of warranties; and 131573Srgrimes * 2. No rights are granted, in any manner or form, to use Whistle 141573Srgrimes * Communications, Inc. trademarks, including the mark "WHISTLE 151573Srgrimes * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 161573Srgrimes * such appears in the above copyright notice or in the software. 171573Srgrimes * 181573Srgrimes * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 191573Srgrimes * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 201573Srgrimes * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 211573Srgrimes * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 221573Srgrimes * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 231573Srgrimes * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 241573Srgrimes * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 251573Srgrimes * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 261573Srgrimes * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 271573Srgrimes * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 281573Srgrimes * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 291573Srgrimes * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 301573Srgrimes * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 311573Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 321573Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 331573Srgrimes * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 341573Srgrimes * OF SUCH DAMAGE. 351573Srgrimes * 361573Srgrimes * Copyright (c) 2000 Junichi SATOH <junichi@astec.co.jp> 371573Srgrimes * <junichi@junichi.org> 381573Srgrimes * All rights reserved. 391573Srgrimes * 4092986Sobrien * Redistribution and use in source and binary forms, with or without 4192986Sobrien * modification, are permitted provided that the following conditions 421573Srgrimes * are met: 431573Srgrimes * 1. Redistributions of source code must retain the above copyright 441573Srgrimes * notice, this list of conditions and the following disclaimer. 451573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 4692905Sobrien * notice, this list of conditions and the following disclaimer in the 4792905Sobrien * documentation and/or other materials provided with the distribution. 4892905Sobrien * 491573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 501573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 511573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5261218Sache * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 531573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 541573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 551573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 561573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5757035Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5857035Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5957035Sobrien * SUCH DAMAGE. 6057035Sobrien * 6157035Sobrien * Authors: Erik Salander <erik@whistle.com> 6257035Sobrien * Junichi SATOH <junichi@astec.co.jp> 6357035Sobrien * <junichi@junichi.org> 6457035Sobrien */ 6557035Sobrien 6657035Sobrien#include <sys/cdefs.h> 6757035Sobrien__FBSDID("$FreeBSD$"); 6857035Sobrien 6957035Sobrien/* 7057035Sobrien Alias_smedia.c is meant to contain the aliasing code for streaming media 7157035Sobrien protocols. It performs special processing for RSTP sessions under TCP. 7257035Sobrien Specifically, when a SETUP request is sent by a client, or a 200 reply 7357035Sobrien is sent by a server, it is intercepted and modified. The address is 7457035Sobrien changed to the gateway machine and an aliasing port is used. 7557035Sobrien 7657035Sobrien More specifically, the "client_port" configuration parameter is 7757035Sobrien parsed for SETUP requests. The "server_port" configuration parameter is 7857035Sobrien parsed for 200 replies eminating from a server. This is intended to handle 7957035Sobrien the unicast case. 8057035Sobrien 8157035Sobrien RTSP also allows a redirection of a stream to another client by using the 8257035Sobrien "destination" configuration parameter. The destination config parm would 8357035Sobrien indicate a different IP address. This function is NOT supported by the 8457035Sobrien RTSP translation code below. 8557035Sobrien 8657035Sobrien The RTSP multicast functions without any address translation intervention. 8757035Sobrien 8857035Sobrien For this routine to work, the SETUP/200 must fit entirely 8957035Sobrien into a single TCP packet. This is typically the case, but exceptions 9057035Sobrien can easily be envisioned under the actual specifications. 9157035Sobrien 9257035Sobrien Probably the most troubling aspect of the approach taken here is 9357035Sobrien that the new SETUP/200 will typically be a different length, and 9457035Sobrien this causes a certain amount of bookkeeping to keep track of the 9557035Sobrien changes of sequence and acknowledgment numbers, since the client 9657035Sobrien machine is totally unaware of the modification to the TCP stream. 9757035Sobrien 9857035Sobrien Initial version: May, 2000 (eds) 9957035Sobrien*/ 10057035Sobrien 10157035Sobrien#ifdef _KERNEL 10257035Sobrien#include <sys/param.h> 10357035Sobrien#include <sys/systm.h> 10457035Sobrien#include <sys/kernel.h> 10557035Sobrien#include <sys/module.h> 10657035Sobrien#else 10757035Sobrien#include <errno.h> 10857035Sobrien#include <sys/types.h> 10957035Sobrien#include <stdio.h> 11057035Sobrien#include <string.h> 11157035Sobrien#endif 11257035Sobrien 11357035Sobrien#include <netinet/in_systm.h> 11457035Sobrien#include <netinet/in.h> 11557035Sobrien#include <netinet/ip.h> 11657035Sobrien#include <netinet/tcp.h> 11757035Sobrien 11857035Sobrien#ifdef _KERNEL 11957035Sobrien#include <netinet/libalias/alias.h> 12057035Sobrien#include <netinet/libalias/alias_local.h> 12157035Sobrien#include <netinet/libalias/alias_mod.h> 12257035Sobrien#else 12357035Sobrien#include "alias_local.h" 12457035Sobrien#include "alias_mod.h" 12557035Sobrien#endif 12657035Sobrien 12757035Sobrien#define RTSP_CONTROL_PORT_NUMBER_1 554 12857035Sobrien#define RTSP_CONTROL_PORT_NUMBER_2 7070 12957035Sobrien#define TFTP_PORT_NUMBER 69 13057035Sobrien 13157035Sobrienstatic void 13257035SobrienAliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, 13357035Sobrien int maxpacketsize); 13457035Sobrienstatic int 13557035Sobrienfingerprint(struct libalias *la, struct alias_data *ah) 13657035Sobrien{ 13757035Sobrien 13857035Sobrien if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL && 13957035Sobrien ntohs(*ah->dport) == TFTP_PORT_NUMBER) 14057035Sobrien return (0); 14157035Sobrien if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || 14257035Sobrien ah->maxpktsize == 0) 14357035Sobrien return (-1); 14457035Sobrien if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1 14557035Sobrien || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1 14657035Sobrien || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2 14757035Sobrien || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2) 14857035Sobrien return (0); 14957035Sobrien return (-1); 15057035Sobrien} 15157035Sobrien 15257035Sobrienstatic int 15357035Sobrienprotohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 15457035Sobrien{ 15557035Sobrien 15657035Sobrien if (ntohs(*ah->dport) == TFTP_PORT_NUMBER) 15757035Sobrien FindRtspOut(la, pip->ip_src, pip->ip_dst, 15857035Sobrien *ah->sport, *ah->aport, IPPROTO_UDP); 15957035Sobrien else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize); 16057035Sobrien return (0); 16157035Sobrien} 16257035Sobrien 16357035Sobrienstruct proto_handler handlers[] = { 16457035Sobrien { 16557035Sobrien .pri = 100, 16657035Sobrien .dir = OUT, 16757035Sobrien .proto = TCP|UDP, 16857035Sobrien .fingerprint = &fingerprint, 16957035Sobrien .protohandler = &protohandler 17057035Sobrien }, 17157035Sobrien { EOH } 17257035Sobrien}; 17357035Sobrien 17457035Sobrienstatic int 17557035Sobrienmod_handler(module_t mod, int type, void *data) 17657035Sobrien{ 17757035Sobrien int error; 17857035Sobrien 17957035Sobrien switch (type) { 18057035Sobrien case MOD_LOAD: 18157035Sobrien error = 0; 18257035Sobrien LibAliasAttachHandlers(handlers); 18357035Sobrien break; 18457035Sobrien case MOD_UNLOAD: 1851573Srgrimes error = 0; 1861573Srgrimes LibAliasDetachHandlers(handlers); 1871573Srgrimes break; 1881573Srgrimes default: 1891573Srgrimes error = EINVAL; 1901573Srgrimes } 1911573Srgrimes return (error); 1921573Srgrimes} 1931573Srgrimes 1941573Srgrimes#ifdef _KERNEL 1951573Srgrimesstatic 1961573Srgrimes#endif 1971573Srgrimesmoduledata_t alias_mod = { 1981573Srgrimes "alias_smedia", mod_handler, NULL 1991573Srgrimes}; 2001573Srgrimes 2011573Srgrimes#ifdef _KERNEL 2021573SrgrimesDECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 2031573SrgrimesMODULE_VERSION(alias_smedia, 1); 2041573SrgrimesMODULE_DEPEND(alias_smedia, libalias, 1, 1, 1); 2051573Srgrimes#endif 2061573Srgrimes 2071573Srgrimes#define RTSP_CONTROL_PORT_NUMBER_1 554 2081573Srgrimes#define RTSP_CONTROL_PORT_NUMBER_2 7070 2091573Srgrimes#define RTSP_PORT_GROUP 2 2101573Srgrimes 2111573Srgrimes#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) 2121573Srgrimes 2131573Srgrimesstatic int 2141573Srgrimessearch_string(char *data, int dlen, const char *search_str) 2151573Srgrimes{ 2161573Srgrimes int i, j, k; 2171573Srgrimes int search_str_len; 2181573Srgrimes 2191573Srgrimes search_str_len = strlen(search_str); 2201573Srgrimes for (i = 0; i < dlen - search_str_len; i++) { 2211573Srgrimes for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { 2221573Srgrimes if (data[j] != search_str[k] && 2231573Srgrimes data[j] != search_str[k] - ('a' - 'A')) { 2241573Srgrimes break; 2251573Srgrimes } 2261573Srgrimes if (k == search_str_len - 1) { 2271573Srgrimes return (j + 1); 2281573Srgrimes } 2291573Srgrimes } 2301573Srgrimes } 2311573Srgrimes return (-1); 2321573Srgrimes} 2331573Srgrimes 2341573Srgrimesstatic int 2351573Srgrimesalias_rtsp_out(struct libalias *la, struct ip *pip, 2361573Srgrimes struct alias_link *lnk, 2371573Srgrimes char *data, 2381573Srgrimes const char *port_str) 2391573Srgrimes{ 2401573Srgrimes int hlen, tlen, dlen; 2411573Srgrimes struct tcphdr *tc; 2421573Srgrimes int i, j, pos, state, port_dlen, new_dlen, delta; 2431573Srgrimes u_short p[2], new_len; 2441573Srgrimes u_short sport, eport, base_port; 2451573Srgrimes u_short salias = 0, ealias = 0, base_alias = 0; 2461573Srgrimes const char *transport_str = "transport:"; 2471573Srgrimes char newdata[2048], *port_data, *port_newdata, stemp[80]; 2481573Srgrimes int links_created = 0, pkt_updated = 0; 2491573Srgrimes struct alias_link *rtsp_lnk = NULL; 2501573Srgrimes struct in_addr null_addr; 2511573Srgrimes 2521573Srgrimes /* Calculate data length of TCP packet */ 2531573Srgrimes tc = (struct tcphdr *)ip_next(pip); 2541573Srgrimes hlen = (pip->ip_hl + tc->th_off) << 2; 2551573Srgrimes tlen = ntohs(pip->ip_len); 2561573Srgrimes dlen = tlen - hlen; 25722478Sache 25822478Sache /* Find keyword, "Transport: " */ 259 pos = search_string(data, dlen, transport_str); 260 if (pos < 0) { 261 return (-1); 262 } 263 port_data = data + pos; 264 port_dlen = dlen - pos; 265 266 memcpy(newdata, data, pos); 267 port_newdata = newdata + pos; 268 269 while (port_dlen > (int)strlen(port_str)) { 270 /* Find keyword, appropriate port string */ 271 pos = search_string(port_data, port_dlen, port_str); 272 if (pos < 0) { 273 break; 274 } 275 memcpy(port_newdata, port_data, pos + 1); 276 port_newdata += (pos + 1); 277 278 p[0] = p[1] = 0; 279 sport = eport = 0; 280 state = 0; 281 for (i = pos; i < port_dlen; i++) { 282 switch (state) { 283 case 0: 284 if (port_data[i] == '=') { 285 state++; 286 } 287 break; 288 case 1: 289 if (ISDIGIT(port_data[i])) { 290 p[0] = p[0] * 10 + port_data[i] - '0'; 291 } else { 292 if (port_data[i] == ';') { 293 state = 3; 294 } 295 if (port_data[i] == '-') { 296 state++; 297 } 298 } 299 break; 300 case 2: 301 if (ISDIGIT(port_data[i])) { 302 p[1] = p[1] * 10 + port_data[i] - '0'; 303 } else { 304 state++; 305 } 306 break; 307 case 3: 308 base_port = p[0]; 309 sport = htons(p[0]); 310 eport = htons(p[1]); 311 312 if (!links_created) { 313 314 links_created = 1; 315 /* 316 * Find an even numbered port 317 * number base that satisfies the 318 * contiguous number of ports we 319 * need 320 */ 321 null_addr.s_addr = 0; 322 if (0 == (salias = FindNewPortGroup(la, null_addr, 323 FindAliasAddress(la, pip->ip_src), 324 sport, 0, 325 RTSP_PORT_GROUP, 326 IPPROTO_UDP, 1))) { 327#ifdef LIBALIAS_DEBUG 328 fprintf(stderr, 329 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 330#endif 331 } else { 332 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 365 pkt_updated = 1; 366 367 /* Copy into IP packet */ 368 sprintf(stemp, "%d", ntohs(salias)); 369 memcpy(port_newdata, stemp, strlen(stemp)); 370 port_newdata += strlen(stemp); 371 372 if (eport != 0) { 373 *port_newdata = '-'; 374 port_newdata++; 375 376 /* Copy into IP packet */ 377 sprintf(stemp, "%d", ntohs(ealias)); 378 memcpy(port_newdata, stemp, strlen(stemp)); 379 port_newdata += strlen(stemp); 380 } 381 *port_newdata = ';'; 382 port_newdata++; 383 } 384 state++; 385 break; 386 } 387 if (state > 3) { 388 break; 389 } 390 } 391 port_data += i; 392 port_dlen -= i; 393 } 394 395 if (!pkt_updated) 396 return (-1); 397 398 memcpy(port_newdata, port_data, port_dlen); 399 port_newdata += port_dlen; 400 *port_newdata = '\0'; 401 402 /* Create new packet */ 403 new_dlen = port_newdata - newdata; 404 memcpy(data, newdata, new_dlen); 405 406 SetAckModified(lnk); 407 tc = (struct tcphdr *)ip_next(pip); 408 delta = GetDeltaSeqOut(tc->th_seq, lnk); 409 AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len, 410 tc->th_seq, tc->th_off); 411 412 new_len = htons(hlen + new_dlen); 413 DifferentialChecksum(&pip->ip_sum, 414 &new_len, 415 &pip->ip_len, 416 1); 417 pip->ip_len = new_len; 418 419 tc->th_sum = 0; 420#ifdef _KERNEL 421 tc->th_x2 = 1; 422#else 423 tc->th_sum = TcpChecksum(pip); 424#endif 425 return (0); 426} 427 428/* Support the protocol used by early versions of RealPlayer */ 429 430static int 431alias_pna_out(struct libalias *la, struct ip *pip, 432 struct alias_link *lnk, 433 char *data, 434 int dlen) 435{ 436 struct alias_link *pna_links; 437 u_short msg_id, msg_len; 438 char *work; 439 u_short alias_port, port; 440 struct tcphdr *tc; 441 442 work = data; 443 work += 5; 444 while (work + 4 < data + dlen) { 445 memcpy(&msg_id, work, 2); 446 work += 2; 447 memcpy(&msg_len, work, 2); 448 work += 2; 449 if (ntohs(msg_id) == 0) { 450 /* end of options */ 451 return (0); 452 } 453 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 454 memcpy(&port, work, 2); 455 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 456 port, 0, IPPROTO_UDP, 1); 457 if (pna_links != NULL) { 458#ifndef NO_FW_PUNCH 459 /* Punch hole in firewall */ 460 PunchFWHole(pna_links); 461#endif 462 tc = (struct tcphdr *)ip_next(pip); 463 alias_port = GetAliasPort(pna_links); 464 memcpy(work, &alias_port, 2); 465 466 /* Compute TCP checksum for revised packet */ 467 tc->th_sum = 0; 468#ifdef _KERNEL 469 tc->th_x2 = 1; 470#else 471 tc->th_sum = TcpChecksum(pip); 472#endif 473 } 474 } 475 work += ntohs(msg_len); 476 } 477 478 return (0); 479} 480 481static void 482AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 483{ 484 int hlen, tlen, dlen; 485 struct tcphdr *tc; 486 char *data; 487 const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 488 const char *okstr = "OK", *client_port_str = "client_port"; 489 const char *server_port_str = "server_port"; 490 int i, parseOk; 491 492 (void)maxpacketsize; 493 494 tc = (struct tcphdr *)ip_next(pip); 495 hlen = (pip->ip_hl + tc->th_off) << 2; 496 tlen = ntohs(pip->ip_len); 497 dlen = tlen - hlen; 498 499 data = (char *)pip; 500 data += hlen; 501 502 /* When aliasing a client, check for the SETUP request */ 503 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 504 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 505 506 if (dlen >= (int)strlen(setup)) { 507 if (memcmp(data, setup, strlen(setup)) == 0) { 508 alias_rtsp_out(la, pip, lnk, data, client_port_str); 509 return; 510 } 511 } 512 if (dlen >= (int)strlen(pna)) { 513 if (memcmp(data, pna, strlen(pna)) == 0) { 514 alias_pna_out(la, pip, lnk, data, dlen); 515 } 516 } 517 } else { 518 519 /* 520 * When aliasing a server, check for the 200 reply 521 * Accomodate varying number of blanks between 200 & OK 522 */ 523 524 if (dlen >= (int)strlen(str200)) { 525 526 for (parseOk = 0, i = 0; 527 i <= dlen - (int)strlen(str200); 528 i++) { 529 if (memcmp(&data[i], str200, strlen(str200)) == 0) { 530 parseOk = 1; 531 break; 532 } 533 } 534 if (parseOk) { 535 536 i += strlen(str200); /* skip string found */ 537 while (data[i] == ' ') /* skip blank(s) */ 538 i++; 539 540 if ((dlen - i) >= (int)strlen(okstr)) { 541 542 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 543 alias_rtsp_out(la, pip, lnk, data, server_port_str); 544 545 } 546 } 547 } 548 } 549} 550