xref: /freebsd/sys/netinet/libalias/alias_smedia.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
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/libkern.h>
104 #else
105 #include <sys/types.h>
106 #include <stdio.h>
107 #include <string.h>
108 #endif
109 
110 #include <netinet/in_systm.h>
111 #include <netinet/in.h>
112 #include <netinet/ip.h>
113 #include <netinet/tcp.h>
114 #include <netinet/udp.h>
115 
116 #ifdef _KERNEL
117 #include <netinet/libalias/alias.h>
118 #include <netinet/libalias/alias_local.h>
119 #else
120 #include "alias_local.h"
121 #endif
122 
123 #define RTSP_CONTROL_PORT_NUMBER_1 554
124 #define RTSP_CONTROL_PORT_NUMBER_2 7070
125 #define RTSP_PORT_GROUP            2
126 
127 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
128 
129 static int
130 search_string(char *data, int dlen, const char *search_str)
131 {
132 	int i, j, k;
133 	int search_str_len;
134 
135 	search_str_len = strlen(search_str);
136 	for (i = 0; i < dlen - search_str_len; i++) {
137 		for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
138 			if (data[j] != search_str[k] &&
139 			    data[j] != search_str[k] - ('a' - 'A')) {
140 				break;
141 			}
142 			if (k == search_str_len - 1) {
143 				return (j + 1);
144 			}
145 		}
146 	}
147 	return (-1);
148 }
149 
150 static int
151 alias_rtsp_out(struct libalias *la, struct ip *pip,
152     struct alias_link *lnk,
153     char *data,
154     const char *port_str)
155 {
156 	int hlen, tlen, dlen;
157 	struct tcphdr *tc;
158 	int i, j, pos, state, port_dlen, new_dlen, delta;
159 	u_short p[2], new_len;
160 	u_short sport, eport, base_port;
161 	u_short salias = 0, ealias = 0, base_alias = 0;
162 	const char *transport_str = "transport:";
163 	char newdata[2048], *port_data, *port_newdata, stemp[80];
164 	int links_created = 0, pkt_updated = 0;
165 	struct alias_link *rtsp_lnk = NULL;
166 	struct in_addr null_addr;
167 
168 	/* Calculate data length of TCP packet */
169 	tc = (struct tcphdr *)ip_next(pip);
170 	hlen = (pip->ip_hl + tc->th_off) << 2;
171 	tlen = ntohs(pip->ip_len);
172 	dlen = tlen - hlen;
173 
174 	/* Find keyword, "Transport: " */
175 	pos = search_string(data, dlen, transport_str);
176 	if (pos < 0) {
177 		return (-1);
178 	}
179 	port_data = data + pos;
180 	port_dlen = dlen - pos;
181 
182 	memcpy(newdata, data, pos);
183 	port_newdata = newdata + pos;
184 
185 	while (port_dlen > (int)strlen(port_str)) {
186 		/* Find keyword, appropriate port string */
187 		pos = search_string(port_data, port_dlen, port_str);
188 		if (pos < 0) {
189 			break;
190 		}
191 		memcpy(port_newdata, port_data, pos + 1);
192 		port_newdata += (pos + 1);
193 
194 		p[0] = p[1] = 0;
195 		sport = eport = 0;
196 		state = 0;
197 		for (i = pos; i < port_dlen; i++) {
198 			switch (state) {
199 			case 0:
200 				if (port_data[i] == '=') {
201 					state++;
202 				}
203 				break;
204 			case 1:
205 				if (ISDIGIT(port_data[i])) {
206 					p[0] = p[0] * 10 + port_data[i] - '0';
207 				} else {
208 					if (port_data[i] == ';') {
209 						state = 3;
210 					}
211 					if (port_data[i] == '-') {
212 						state++;
213 					}
214 				}
215 				break;
216 			case 2:
217 				if (ISDIGIT(port_data[i])) {
218 					p[1] = p[1] * 10 + port_data[i] - '0';
219 				} else {
220 					state++;
221 				}
222 				break;
223 			case 3:
224 				base_port = p[0];
225 				sport = htons(p[0]);
226 				eport = htons(p[1]);
227 
228 				if (!links_created) {
229 
230 					links_created = 1;
231 					/*
232 					 * Find an even numbered port
233 					 * number base that satisfies the
234 					 * contiguous number of ports we
235 					 * need
236 					 */
237 					null_addr.s_addr = 0;
238 					if (0 == (salias = FindNewPortGroup(la, null_addr,
239 					    FindAliasAddress(la, pip->ip_src),
240 					    sport, 0,
241 					    RTSP_PORT_GROUP,
242 					    IPPROTO_UDP, 1))) {
243 #ifdef LIBALIAS_DEBUG
244 						fprintf(stderr,
245 						    "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
246 #endif
247 					} else {
248 
249 						base_alias = ntohs(salias);
250 						for (j = 0; j < RTSP_PORT_GROUP; j++) {
251 							/*
252 							 * Establish link
253 							 * to port found in
254 							 * RTSP packet
255 							 */
256 							rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
257 							    htons(base_port + j), htons(base_alias + j),
258 							    IPPROTO_UDP);
259 							if (rtsp_lnk != NULL) {
260 #ifndef NO_FW_PUNCH
261 								/*
262 								 * Punch
263 								 * hole in
264 								 * firewall
265 								 */
266 								PunchFWHole(rtsp_lnk);
267 #endif
268 							} else {
269 #ifdef LIBALIAS_DEBUG
270 								fprintf(stderr,
271 								    "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
272 #endif
273 								break;
274 							}
275 						}
276 					}
277 					ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
278 				}
279 				if (salias && rtsp_lnk) {
280 
281 					pkt_updated = 1;
282 
283 					/* Copy into IP packet */
284 					sprintf(stemp, "%d", ntohs(salias));
285 					memcpy(port_newdata, stemp, strlen(stemp));
286 					port_newdata += strlen(stemp);
287 
288 					if (eport != 0) {
289 						*port_newdata = '-';
290 						port_newdata++;
291 
292 						/* Copy into IP packet */
293 						sprintf(stemp, "%d", ntohs(ealias));
294 						memcpy(port_newdata, stemp, strlen(stemp));
295 						port_newdata += strlen(stemp);
296 					}
297 					*port_newdata = ';';
298 					port_newdata++;
299 				}
300 				state++;
301 				break;
302 			}
303 			if (state > 3) {
304 				break;
305 			}
306 		}
307 		port_data += i;
308 		port_dlen -= i;
309 	}
310 
311 	if (!pkt_updated)
312 		return (-1);
313 
314 	memcpy(port_newdata, port_data, port_dlen);
315 	port_newdata += port_dlen;
316 	*port_newdata = '\0';
317 
318 	/* Create new packet */
319 	new_dlen = port_newdata - newdata;
320 	memcpy(data, newdata, new_dlen);
321 
322 	SetAckModified(lnk);
323 	delta = GetDeltaSeqOut(pip, lnk);
324 	AddSeq(pip, lnk, delta + new_dlen - dlen);
325 
326 	new_len = htons(hlen + new_dlen);
327 	DifferentialChecksum(&pip->ip_sum,
328 	    &new_len,
329 	    &pip->ip_len,
330 	    1);
331 	pip->ip_len = new_len;
332 
333 	tc->th_sum = 0;
334 #ifdef _KERNEL
335 	tc->th_x2 = 1;
336 #else
337 	tc->th_sum = TcpChecksum(pip);
338 #endif
339 	return (0);
340 }
341 
342 /* Support the protocol used by early versions of RealPlayer */
343 
344 static int
345 alias_pna_out(struct libalias *la, struct ip *pip,
346     struct alias_link *lnk,
347     char *data,
348     int dlen)
349 {
350 	struct alias_link *pna_links;
351 	u_short msg_id, msg_len;
352 	char *work;
353 	u_short alias_port, port;
354 	struct tcphdr *tc;
355 
356 	work = data;
357 	work += 5;
358 	while (work + 4 < data + dlen) {
359 		memcpy(&msg_id, work, 2);
360 		work += 2;
361 		memcpy(&msg_len, work, 2);
362 		work += 2;
363 		if (ntohs(msg_id) == 0) {
364 			/* end of options */
365 			return (0);
366 		}
367 		if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
368 			memcpy(&port, work, 2);
369 			pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
370 			    port, 0, IPPROTO_UDP, 1);
371 			if (pna_links != NULL) {
372 #ifndef NO_FW_PUNCH
373 				/* Punch hole in firewall */
374 				PunchFWHole(pna_links);
375 #endif
376 				tc = (struct tcphdr *)ip_next(pip);
377 				alias_port = GetAliasPort(pna_links);
378 				memcpy(work, &alias_port, 2);
379 
380 				/* Compute TCP checksum for revised packet */
381 				tc->th_sum = 0;
382 #ifdef _KERNEL
383 				tc->th_x2 = 1;
384 #else
385 				tc->th_sum = TcpChecksum(pip);
386 #endif
387 			}
388 		}
389 		work += ntohs(msg_len);
390 	}
391 
392 	return (0);
393 }
394 
395 void
396 AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
397 {
398 	int hlen, tlen, dlen;
399 	struct tcphdr *tc;
400 	char *data;
401 	const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
402 	const char *okstr = "OK", *client_port_str = "client_port";
403 	const char *server_port_str = "server_port";
404 	int i, parseOk;
405 
406 	(void)maxpacketsize;
407 
408 	tc = (struct tcphdr *)ip_next(pip);
409 	hlen = (pip->ip_hl + tc->th_off) << 2;
410 	tlen = ntohs(pip->ip_len);
411 	dlen = tlen - hlen;
412 
413 	data = (char *)pip;
414 	data += hlen;
415 
416 	/* When aliasing a client, check for the SETUP request */
417 	if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
418 	    (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
419 
420 		if (dlen >= (int)strlen(setup)) {
421 			if (memcmp(data, setup, strlen(setup)) == 0) {
422 				alias_rtsp_out(la, pip, lnk, data, client_port_str);
423 				return;
424 			}
425 		}
426 		if (dlen >= (int)strlen(pna)) {
427 			if (memcmp(data, pna, strlen(pna)) == 0) {
428 				alias_pna_out(la, pip, lnk, data, dlen);
429 			}
430 		}
431 	} else {
432 
433 		/*
434 		 * When aliasing a server, check for the 200 reply
435 		 * Accomodate varying number of blanks between 200 & OK
436 		 */
437 
438 		if (dlen >= (int)strlen(str200)) {
439 
440 			for (parseOk = 0, i = 0;
441 			    i <= dlen - (int)strlen(str200);
442 			    i++) {
443 				if (memcmp(&data[i], str200, strlen(str200)) == 0) {
444 					parseOk = 1;
445 					break;
446 				}
447 			}
448 			if (parseOk) {
449 
450 				i += strlen(str200);	/* skip string found */
451 				while (data[i] == ' ')	/* skip blank(s) */
452 					i++;
453 
454 				if ((dlen - i) >= (int)strlen(okstr)) {
455 
456 					if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
457 						alias_rtsp_out(la, pip, lnk, data, server_port_str);
458 
459 				}
460 			}
461 		}
462 	}
463 }
464