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