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 if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL && 140 ntohs(*ah->dport) == TFTP_PORT_NUMBER) 141 return (0); 142 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || 143 ah->maxpktsize == 0) 144 return (-1); 145 if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1 146 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1 147 || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2 148 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2) 149 return (0); 150 return (-1); 151 } 152 153 static int 154 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 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 if (k == search_str_len - 1) 226 return (j + 1); 227 } 228 } 229 return (-1); 230 } 231 232 static int 233 alias_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 break; 285 case 1: 286 if (ISDIGIT(port_data[i])) 287 p[0] = p[0] * 10 + port_data[i] - '0'; 288 else if (port_data[i] == ';') 289 state = 3; 290 else if (port_data[i] == '-') 291 state++; 292 break; 293 case 2: 294 if (ISDIGIT(port_data[i])) 295 p[1] = p[1] * 10 + port_data[i] - '0'; 296 else 297 state++; 298 break; 299 case 3: 300 base_port = p[0]; 301 sport = htons(p[0]); 302 eport = htons(p[1]); 303 304 if (!links_created) { 305 links_created = 1; 306 /* 307 * Find an even numbered port 308 * number base that satisfies the 309 * contiguous number of ports we 310 * need 311 */ 312 null_addr.s_addr = 0; 313 if (0 == (salias = FindNewPortGroup(la, null_addr, 314 FindAliasAddress(la, pip->ip_src), 315 sport, 0, 316 RTSP_PORT_GROUP, 317 IPPROTO_UDP, 1))) { 318 #ifdef LIBALIAS_DEBUG 319 fprintf(stderr, 320 "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); 321 #endif 322 } else { 323 base_alias = ntohs(salias); 324 for (j = 0; j < RTSP_PORT_GROUP; j++) { 325 /* 326 * Establish link 327 * to port found in 328 * RTSP packet 329 */ 330 rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, 331 htons(base_port + j), htons(base_alias + j), 332 IPPROTO_UDP); 333 if (rtsp_lnk != NULL) { 334 #ifndef NO_FW_PUNCH 335 /* 336 * Punch 337 * hole in 338 * firewall 339 */ 340 PunchFWHole(rtsp_lnk); 341 #endif 342 } else { 343 #ifdef LIBALIAS_DEBUG 344 fprintf(stderr, 345 "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); 346 #endif 347 break; 348 } 349 } 350 } 351 ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); 352 } 353 if (salias && rtsp_lnk) { 354 pkt_updated = 1; 355 356 /* Copy into IP packet */ 357 sprintf(stemp, "%d", ntohs(salias)); 358 memcpy(port_newdata, stemp, strlen(stemp)); 359 port_newdata += strlen(stemp); 360 361 if (eport != 0) { 362 *port_newdata = '-'; 363 port_newdata++; 364 365 /* Copy into IP packet */ 366 sprintf(stemp, "%d", ntohs(ealias)); 367 memcpy(port_newdata, stemp, strlen(stemp)); 368 port_newdata += strlen(stemp); 369 } 370 *port_newdata = ';'; 371 port_newdata++; 372 } 373 state++; 374 break; 375 } 376 if (state > 3) { 377 break; 378 } 379 } 380 port_data += i; 381 port_dlen -= i; 382 } 383 384 if (!pkt_updated) 385 return (-1); 386 387 memcpy(port_newdata, port_data, port_dlen); 388 port_newdata += port_dlen; 389 *port_newdata = '\0'; 390 391 /* Create new packet */ 392 new_dlen = port_newdata - newdata; 393 memcpy(data, newdata, new_dlen); 394 395 SetAckModified(lnk); 396 tc = (struct tcphdr *)ip_next(pip); 397 delta = GetDeltaSeqOut(tc->th_seq, lnk); 398 AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len, 399 tc->th_seq, tc->th_off); 400 401 new_len = htons(hlen + new_dlen); 402 DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); 403 pip->ip_len = new_len; 404 405 tc->th_sum = 0; 406 #ifdef _KERNEL 407 tc->th_x2 = 1; 408 #else 409 tc->th_sum = TcpChecksum(pip); 410 #endif 411 return (0); 412 } 413 414 /* Support the protocol used by early versions of RealPlayer */ 415 416 static int 417 alias_pna_out(struct libalias *la, struct ip *pip, 418 struct alias_link *lnk, 419 char *data, 420 int dlen) 421 { 422 struct alias_link *pna_links; 423 u_short msg_id, msg_len; 424 char *work; 425 u_short alias_port, port; 426 struct tcphdr *tc; 427 428 work = data; 429 work += 5; 430 while (work + 4 < data + dlen) { 431 memcpy(&msg_id, work, 2); 432 work += 2; 433 memcpy(&msg_len, work, 2); 434 work += 2; 435 if (ntohs(msg_id) == 0) /* end of options */ 436 return (0); 437 438 if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { 439 memcpy(&port, work, 2); 440 pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), 441 port, 0, IPPROTO_UDP, 1); 442 if (pna_links != NULL) { 443 #ifndef NO_FW_PUNCH 444 /* Punch hole in firewall */ 445 PunchFWHole(pna_links); 446 #endif 447 tc = (struct tcphdr *)ip_next(pip); 448 alias_port = GetAliasPort(pna_links); 449 memcpy(work, &alias_port, 2); 450 451 /* Compute TCP checksum for revised packet */ 452 tc->th_sum = 0; 453 #ifdef _KERNEL 454 tc->th_x2 = 1; 455 #else 456 tc->th_sum = TcpChecksum(pip); 457 #endif 458 } 459 } 460 work += ntohs(msg_len); 461 } 462 463 return (0); 464 } 465 466 static void 467 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) 468 { 469 int hlen, tlen, dlen; 470 struct tcphdr *tc; 471 char *data; 472 const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; 473 const char *okstr = "OK", *client_port_str = "client_port"; 474 const char *server_port_str = "server_port"; 475 int i, parseOk; 476 477 (void)maxpacketsize; 478 479 tc = (struct tcphdr *)ip_next(pip); 480 hlen = (pip->ip_hl + tc->th_off) << 2; 481 tlen = ntohs(pip->ip_len); 482 dlen = tlen - hlen; 483 484 data = (char *)pip; 485 data += hlen; 486 487 /* When aliasing a client, check for the SETUP request */ 488 if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || 489 (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { 490 if (dlen >= (int)strlen(setup) && 491 memcmp(data, setup, strlen(setup)) == 0) { 492 alias_rtsp_out(la, pip, lnk, data, client_port_str); 493 return; 494 } 495 496 if (dlen >= (int)strlen(pna) && 497 memcmp(data, pna, strlen(pna)) == 0) 498 alias_pna_out(la, pip, lnk, data, dlen); 499 } else { 500 /* 501 * When aliasing a server, check for the 200 reply 502 * Accommodate varying number of blanks between 200 & OK 503 */ 504 505 if (dlen >= (int)strlen(str200)) { 506 for (parseOk = 0, i = 0; 507 i <= dlen - (int)strlen(str200); 508 i++) 509 if (memcmp(&data[i], str200, strlen(str200)) == 0) { 510 parseOk = 1; 511 break; 512 } 513 514 if (parseOk) { 515 i += strlen(str200); /* skip string found */ 516 while (data[i] == ' ') /* skip blank(s) */ 517 i++; 518 519 if ((dlen - i) >= (int)strlen(okstr)) 520 if (memcmp(&data[i], okstr, strlen(okstr)) == 0) 521 alias_rtsp_out(la, pip, lnk, data, server_port_str); 522 } 523 } 524 } 525 } 526