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$"); 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 131 static void 132 AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, 133 int maxpacketsize); 134 static int 135 fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah) 136 { 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 156 if (ntohs(*ah->dport) == TFTP_PORT_NUMBER) 157 FindRtspOut(la, pip->ip_src, pip->ip_dst, 158 *ah->sport, *ah->aport, IPPROTO_UDP); 159 else 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 int 214 search_string(char *data, int dlen, const char *search_str) 215 { 216 int i, j, k; 217 int search_str_len; 218 219 search_str_len = strlen(search_str); 220 for (i = 0; i < dlen - search_str_len; i++) { 221 for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { 222 if (data[j] != search_str[k] && 223 data[j] != search_str[k] - ('a' - 'A')) { 224 break; 225 } 226 if (k == search_str_len - 1) { 227 return (j + 1); 228 } 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, 238 const char *port_str) 239 { 240 int hlen, tlen, dlen; 241 struct tcphdr *tc; 242 int i, j, pos, state, port_dlen, new_dlen, delta; 243 u_short p[2], new_len; 244 u_short sport, eport, base_port; 245 u_short salias = 0, ealias = 0, base_alias = 0; 246 const char *transport_str = "transport:"; 247 char newdata[2048], *port_data, *port_newdata, stemp[80]; 248 int links_created = 0, pkt_updated = 0; 249 struct alias_link *rtsp_lnk = NULL; 250 struct in_addr null_addr; 251 252 /* Calculate data length of TCP packet */ 253 tc = (struct tcphdr *)ip_next(pip); 254 hlen = (pip->ip_hl + tc->th_off) << 2; 255 tlen = ntohs(pip->ip_len); 256 dlen = tlen - hlen; 257 258 /* 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 delta = GetDeltaSeqOut(pip, lnk); 408 AddSeq(pip, lnk, delta + new_dlen - dlen); 409 410 new_len = htons(hlen + new_dlen); 411 DifferentialChecksum(&pip->ip_sum, 412 &new_len, 413 &pip->ip_len, 414 1); 415 pip->ip_len = new_len; 416 417 tc->th_sum = 0; 418 #ifdef _KERNEL 419 tc->th_x2 = 1; 420 #else 421 tc->th_sum = TcpChecksum(pip); 422 #endif 423 return (0); 424 } 425 426 /* Support the protocol used by early versions of RealPlayer */ 427 428 static int 429 alias_pna_out(struct libalias *la, struct ip *pip, 430 struct alias_link *lnk, 431 char *data, 432 int dlen) 433 { 434 struct alias_link *pna_links; 435 u_short msg_id, msg_len; 436 char *work; 437 u_short alias_port, port; 438 struct tcphdr *tc; 439 440 work = data; 441 work += 5; 442 while (work + 4 < data + dlen) { 443 memcpy(&msg_id, work, 2); 444 work += 2; 445 memcpy(&msg_len, work, 2); 446 work += 2; 447 if (ntohs(msg_id) == 0) { 448 /* end of options */ 449 return (0); 450 } 451 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 452 memcpy(&port, work, 2); 453 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 454 port, 0, IPPROTO_UDP, 1); 455 if (pna_links != NULL) { 456 #ifndef NO_FW_PUNCH 457 /* Punch hole in firewall */ 458 PunchFWHole(pna_links); 459 #endif 460 tc = (struct tcphdr *)ip_next(pip); 461 alias_port = GetAliasPort(pna_links); 462 memcpy(work, &alias_port, 2); 463 464 /* Compute TCP checksum for revised packet */ 465 tc->th_sum = 0; 466 #ifdef _KERNEL 467 tc->th_x2 = 1; 468 #else 469 tc->th_sum = TcpChecksum(pip); 470 #endif 471 } 472 } 473 work += ntohs(msg_len); 474 } 475 476 return (0); 477 } 478 479 static void 480 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 481 { 482 int hlen, tlen, dlen; 483 struct tcphdr *tc; 484 char *data; 485 const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 486 const char *okstr = "OK", *client_port_str = "client_port"; 487 const char *server_port_str = "server_port"; 488 int i, parseOk; 489 490 (void)maxpacketsize; 491 492 tc = (struct tcphdr *)ip_next(pip); 493 hlen = (pip->ip_hl + tc->th_off) << 2; 494 tlen = ntohs(pip->ip_len); 495 dlen = tlen - hlen; 496 497 data = (char *)pip; 498 data += hlen; 499 500 /* When aliasing a client, check for the SETUP request */ 501 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 502 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 503 504 if (dlen >= (int)strlen(setup)) { 505 if (memcmp(data, setup, strlen(setup)) == 0) { 506 alias_rtsp_out(la, pip, lnk, data, client_port_str); 507 return; 508 } 509 } 510 if (dlen >= (int)strlen(pna)) { 511 if (memcmp(data, pna, strlen(pna)) == 0) { 512 alias_pna_out(la, pip, lnk, data, dlen); 513 } 514 } 515 } else { 516 517 /* 518 * When aliasing a server, check for the 200 reply 519 * Accomodate varying number of blanks between 200 & OK 520 */ 521 522 if (dlen >= (int)strlen(str200)) { 523 524 for (parseOk = 0, i = 0; 525 i <= dlen - (int)strlen(str200); 526 i++) { 527 if (memcmp(&data[i], str200, strlen(str200)) == 0) { 528 parseOk = 1; 529 break; 530 } 531 } 532 if (parseOk) { 533 534 i += strlen(str200); /* skip string found */ 535 while (data[i] == ' ') /* skip blank(s) */ 536 i++; 537 538 if ((dlen - i) >= (int)strlen(okstr)) { 539 540 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 541 alias_rtsp_out(la, pip, lnk, data, server_port_str); 542 543 } 544 } 545 } 546 } 547 } 548