alias_smedia.c revision 162674
1/* 2 * alias_smedia.c 3 * 4 * Copyright (c) 2000 Whistle Communications, Inc. 5 * All rights reserved. 6 * 7 * Subject to the following obligations and disclaimer of warranty, use and 8 * redistribution of this software, in source or object code forms, with or 9 * without modifications are expressly permitted by Whistle Communications; 10 * provided, however, that: 11 * 1. Any and all reproductions of the source or object code must include the 12 * copyright notice above and the following disclaimer of warranties; and 13 * 2. No rights are granted, in any manner or form, to use Whistle 14 * Communications, Inc. trademarks, including the mark "WHISTLE 15 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 16 * such appears in the above copyright notice or in the software. 17 * 18 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 19 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 20 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 21 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 23 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 24 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 25 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 26 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 27 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 28 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 29 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 30 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 34 * OF SUCH DAMAGE. 35 * 36 * Copyright (c) 2000 Junichi SATOH <junichi@astec.co.jp> 37 * <junichi@junichi.org> 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 * 61 * Authors: Erik Salander <erik@whistle.com> 62 * Junichi SATOH <junichi@astec.co.jp> 63 * <junichi@junichi.org> 64 */ 65 66#include <sys/cdefs.h> 67__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_smedia.c 162674 2006-09-26 23:26:53Z piso $"); 68 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 131static void 132AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, 133 int maxpacketsize); 134static int 135fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah) 136{ 137 138 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || 139 ah->maxpktsize == 0) 140 return (-1); 141 if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1 142 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1 143 || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2 144 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2 145 || ntohs(*ah->dport) == TFTP_PORT_NUMBER) 146 return (0); 147 return (-1); 148} 149 150static int 151protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 152{ 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 161struct 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 172static int 173mod_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 193static 194#endif 195moduledata_t alias_mod = { 196 "alias_smedia", mod_handler, NULL 197}; 198 199#ifdef _KERNEL 200DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 201MODULE_VERSION(alias_smedia, 1); 202MODULE_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 211static int 212search_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 } 224 if (k == search_str_len - 1) { 225 return (j + 1); 226 } 227 } 228 } 229 return (-1); 230} 231 232static int 233alias_rtsp_out(struct libalias *la, struct ip *pip, 234 struct alias_link *lnk, 235 char *data, 236 const char *port_str) 237{ 238 int hlen, tlen, dlen; 239 struct tcphdr *tc; 240 int i, j, pos, state, port_dlen, new_dlen, delta; 241 u_short p[2], new_len; 242 u_short sport, eport, base_port; 243 u_short salias = 0, ealias = 0, base_alias = 0; 244 const char *transport_str = "transport:"; 245 char newdata[2048], *port_data, *port_newdata, stemp[80]; 246 int links_created = 0, pkt_updated = 0; 247 struct alias_link *rtsp_lnk = NULL; 248 struct in_addr null_addr; 249 250 /* Calculate data length of TCP packet */ 251 tc = (struct tcphdr *)ip_next(pip); 252 hlen = (pip->ip_hl + tc->th_off) << 2; 253 tlen = ntohs(pip->ip_len); 254 dlen = tlen - hlen; 255 256 /* Find keyword, "Transport: " */ 257 pos = search_string(data, dlen, transport_str); 258 if (pos < 0) { 259 return (-1); 260 } 261 port_data = data + pos; 262 port_dlen = dlen - pos; 263 264 memcpy(newdata, data, pos); 265 port_newdata = newdata + pos; 266 267 while (port_dlen > (int)strlen(port_str)) { 268 /* Find keyword, appropriate port string */ 269 pos = search_string(port_data, port_dlen, port_str); 270 if (pos < 0) { 271 break; 272 } 273 memcpy(port_newdata, port_data, pos + 1); 274 port_newdata += (pos + 1); 275 276 p[0] = p[1] = 0; 277 sport = eport = 0; 278 state = 0; 279 for (i = pos; i < port_dlen; i++) { 280 switch (state) { 281 case 0: 282 if (port_data[i] == '=') { 283 state++; 284 } 285 break; 286 case 1: 287 if (ISDIGIT(port_data[i])) { 288 p[0] = p[0] * 10 + port_data[i] - '0'; 289 } else { 290 if (port_data[i] == ';') { 291 state = 3; 292 } 293 if (port_data[i] == '-') { 294 state++; 295 } 296 } 297 break; 298 case 2: 299 if (ISDIGIT(port_data[i])) { 300 p[1] = p[1] * 10 + port_data[i] - '0'; 301 } else { 302 state++; 303 } 304 break; 305 case 3: 306 base_port = p[0]; 307 sport = htons(p[0]); 308 eport = htons(p[1]); 309 310 if (!links_created) { 311 312 links_created = 1; 313 /* 314 * Find an even numbered port 315 * number base that satisfies the 316 * contiguous number of ports we 317 * need 318 */ 319 null_addr.s_addr = 0; 320 if (0 == (salias = FindNewPortGroup(la, null_addr, 321 FindAliasAddress(la, pip->ip_src), 322 sport, 0, 323 RTSP_PORT_GROUP, 324 IPPROTO_UDP, 1))) { 325#ifdef LIBALIAS_DEBUG 326 fprintf(stderr, 327 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 328#endif 329 } else { 330 331 base_alias = ntohs(salias); 332 for (j = 0; j < RTSP_PORT_GROUP; j++) { 333 /* 334 * Establish link 335 * to port found in 336 * RTSP packet 337 */ 338 rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, 339 htons(base_port + j), htons(base_alias + j), 340 IPPROTO_UDP); 341 if (rtsp_lnk != NULL) { 342#ifndef NO_FW_PUNCH 343 /* 344 * Punch 345 * hole in 346 * firewall 347 */ 348 PunchFWHole(rtsp_lnk); 349#endif 350 } else { 351#ifdef LIBALIAS_DEBUG 352 fprintf(stderr, 353 "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); 354#endif 355 break; 356 } 357 } 358 } 359 ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); 360 } 361 if (salias && rtsp_lnk) { 362 363 pkt_updated = 1; 364 365 /* Copy into IP packet */ 366 sprintf(stemp, "%d", ntohs(salias)); 367 memcpy(port_newdata, stemp, strlen(stemp)); 368 port_newdata += strlen(stemp); 369 370 if (eport != 0) { 371 *port_newdata = '-'; 372 port_newdata++; 373 374 /* Copy into IP packet */ 375 sprintf(stemp, "%d", ntohs(ealias)); 376 memcpy(port_newdata, stemp, strlen(stemp)); 377 port_newdata += strlen(stemp); 378 } 379 *port_newdata = ';'; 380 port_newdata++; 381 } 382 state++; 383 break; 384 } 385 if (state > 3) { 386 break; 387 } 388 } 389 port_data += i; 390 port_dlen -= i; 391 } 392 393 if (!pkt_updated) 394 return (-1); 395 396 memcpy(port_newdata, port_data, port_dlen); 397 port_newdata += port_dlen; 398 *port_newdata = '\0'; 399 400 /* Create new packet */ 401 new_dlen = port_newdata - newdata; 402 memcpy(data, newdata, new_dlen); 403 404 SetAckModified(lnk); 405 delta = GetDeltaSeqOut(pip, lnk); 406 AddSeq(pip, lnk, delta + new_dlen - dlen); 407 408 new_len = htons(hlen + new_dlen); 409 DifferentialChecksum(&pip->ip_sum, 410 &new_len, 411 &pip->ip_len, 412 1); 413 pip->ip_len = new_len; 414 415 tc->th_sum = 0; 416#ifdef _KERNEL 417 tc->th_x2 = 1; 418#else 419 tc->th_sum = TcpChecksum(pip); 420#endif 421 return (0); 422} 423 424/* Support the protocol used by early versions of RealPlayer */ 425 426static int 427alias_pna_out(struct libalias *la, struct ip *pip, 428 struct alias_link *lnk, 429 char *data, 430 int dlen) 431{ 432 struct alias_link *pna_links; 433 u_short msg_id, msg_len; 434 char *work; 435 u_short alias_port, port; 436 struct tcphdr *tc; 437 438 work = data; 439 work += 5; 440 while (work + 4 < data + dlen) { 441 memcpy(&msg_id, work, 2); 442 work += 2; 443 memcpy(&msg_len, work, 2); 444 work += 2; 445 if (ntohs(msg_id) == 0) { 446 /* end of options */ 447 return (0); 448 } 449 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 450 memcpy(&port, work, 2); 451 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 452 port, 0, IPPROTO_UDP, 1); 453 if (pna_links != NULL) { 454#ifndef NO_FW_PUNCH 455 /* Punch hole in firewall */ 456 PunchFWHole(pna_links); 457#endif 458 tc = (struct tcphdr *)ip_next(pip); 459 alias_port = GetAliasPort(pna_links); 460 memcpy(work, &alias_port, 2); 461 462 /* Compute TCP checksum for revised packet */ 463 tc->th_sum = 0; 464#ifdef _KERNEL 465 tc->th_x2 = 1; 466#else 467 tc->th_sum = TcpChecksum(pip); 468#endif 469 } 470 } 471 work += ntohs(msg_len); 472 } 473 474 return (0); 475} 476 477static void 478AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 479{ 480 int hlen, tlen, dlen; 481 struct tcphdr *tc; 482 char *data; 483 const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 484 const char *okstr = "OK", *client_port_str = "client_port"; 485 const char *server_port_str = "server_port"; 486 int i, parseOk; 487 488 (void)maxpacketsize; 489 490 tc = (struct tcphdr *)ip_next(pip); 491 hlen = (pip->ip_hl + tc->th_off) << 2; 492 tlen = ntohs(pip->ip_len); 493 dlen = tlen - hlen; 494 495 data = (char *)pip; 496 data += hlen; 497 498 /* When aliasing a client, check for the SETUP request */ 499 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 500 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 501 502 if (dlen >= (int)strlen(setup)) { 503 if (memcmp(data, setup, strlen(setup)) == 0) { 504 alias_rtsp_out(la, pip, lnk, data, client_port_str); 505 return; 506 } 507 } 508 if (dlen >= (int)strlen(pna)) { 509 if (memcmp(data, pna, strlen(pna)) == 0) { 510 alias_pna_out(la, pip, lnk, data, dlen); 511 } 512 } 513 } else { 514 515 /* 516 * When aliasing a server, check for the 200 reply 517 * Accomodate varying number of blanks between 200 & OK 518 */ 519 520 if (dlen >= (int)strlen(str200)) { 521 522 for (parseOk = 0, i = 0; 523 i <= dlen - (int)strlen(str200); 524 i++) { 525 if (memcmp(&data[i], str200, strlen(str200)) == 0) { 526 parseOk = 1; 527 break; 528 } 529 } 530 if (parseOk) { 531 532 i += strlen(str200); /* skip string found */ 533 while (data[i] == ' ') /* skip blank(s) */ 534 i++; 535 536 if ((dlen - i) >= (int)strlen(okstr)) { 537 538 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 539 alias_rtsp_out(la, pip, lnk, data, server_port_str); 540 541 } 542 } 543 } 544 } 545} 546