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