xref: /dragonfly/lib/libalias/alias_smedia.c (revision 73610d44)
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  * $FreeBSD: src/lib/libalias/alias_smedia.c,v 1.1.2.4 2001/03/05 03:48:00 kris Exp $
66  * $DragonFly: src/lib/libalias/alias_smedia.c,v 1.3 2004/08/20 00:08:17 joerg Exp $
67  */
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 <sys/param.h>
102 #include <stdio.h>
103 #include <string.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 ip *pip,
141 		   struct alias_link *link,
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_link = NULL;
155     struct in_addr null_addr;
156 
157     /* Calculate data length of TCP packet */
158     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
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 > 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 
181 	memcpy (port_newdata, port_data, pos + 1);
182 	port_newdata += (pos + 1);
183 
184 	p[0] = p[1] = 0;
185 	sport = eport = 0;
186 	state = 0;
187 	for (i = pos; i < port_dlen; i++) {
188 	    switch(state) {
189 	    case 0:
190 		if (port_data[i] == '=') {
191 		    state++;
192 		}
193 		break;
194 	    case 1:
195 		if (ISDIGIT(port_data[i])) {
196 		    p[0] = p[0] * 10 + port_data[i] - '0';
197 		} else {
198 		    if (port_data[i] == ';') {
199 			state = 3;
200 		    }
201 		    if (port_data[i] == '-') {
202 			state++;
203 		    }
204 		}
205 		break;
206 	    case 2:
207 		if (ISDIGIT(port_data[i])) {
208 		    p[1] = p[1] * 10 + port_data[i] - '0';
209 		} else {
210 		    state++;
211 		}
212 		break;
213 	    case 3:
214 		base_port = p[0];
215 		sport = htons(p[0]);
216 		eport = htons(p[1]);
217 
218 		if (!links_created) {
219 
220 	  	  links_created = 1;
221 		  /* Find an even numbered port number base that
222 		     satisfies the contiguous number of ports we need  */
223 		  null_addr.s_addr = 0;
224 		  if (0 == (salias = FindNewPortGroup(null_addr,
225 	       			    FindAliasAddress(pip->ip_src),
226 				    sport, 0,
227 				    RTSP_PORT_GROUP,
228 				    IPPROTO_UDP, 1))) {
229 #ifdef DEBUG
230 		    fprintf(stderr,
231 		    "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
232 #endif
233 		  } else {
234 
235   		    base_alias = ntohs(salias);
236 		    for (j = 0; j < RTSP_PORT_GROUP; j++) {
237 		      /* Establish link to port found in RTSP packet */
238 		      rtsp_link = FindRtspOut(GetOriginalAddress(link), null_addr,
239                                 htons(base_port + j), htons(base_alias + j),
240                                 IPPROTO_UDP);
241 		      if (rtsp_link != NULL) {
242 #ifndef NO_FW_PUNCH
243 		        /* Punch hole in firewall */
244 		        PunchFWHole(rtsp_link);
245 #endif
246 		      } else {
247 #ifdef DEBUG
248 		        fprintf(stderr,
249 		        "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
250 #endif
251 		        break;
252 		      }
253 		    }
254 		  }
255                   ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
256 		}
257 
258 		if (salias && rtsp_link) {
259 
260 		  pkt_updated = 1;
261 
262 	          /* Copy into IP packet */
263 		  sprintf(stemp, "%d", ntohs(salias));
264 		  memcpy(port_newdata, stemp, strlen(stemp));
265 		  port_newdata += strlen(stemp);
266 
267 		  if (eport != 0) {
268 		    *port_newdata = '-';
269 		    port_newdata++;
270 
271 		    /* Copy into IP packet */
272 		    sprintf(stemp, "%d", ntohs(ealias));
273 		    memcpy(port_newdata, stemp, strlen(stemp));
274 		    port_newdata += strlen(stemp);
275 		  }
276 
277 	          *port_newdata = ';';
278 		  port_newdata++;
279 		}
280 		state++;
281 		break;
282 	    }
283 	    if (state > 3) {
284 		break;
285 	    }
286 	}
287 	port_data += i;
288 	port_dlen -= i;
289     }
290 
291     if (!pkt_updated)
292       return -1;
293 
294     memcpy (port_newdata, port_data, port_dlen);
295     port_newdata += port_dlen;
296     *port_newdata = '\0';
297 
298     /* Create new packet */
299     new_dlen = port_newdata - newdata;
300     memcpy (data, newdata, new_dlen);
301 
302     SetAckModified(link);
303     delta = GetDeltaSeqOut(pip, link);
304     AddSeq(pip, link, delta + new_dlen - dlen);
305 
306     new_len = htons(hlen + new_dlen);
307     DifferentialChecksum(&pip->ip_sum,
308 			 &new_len,
309 			 &pip->ip_len,
310 			 1);
311     pip->ip_len = new_len;
312 
313     tc->th_sum = 0;
314     tc->th_sum = TcpChecksum(pip);
315 
316     return 0;
317 }
318 
319 /* Support the protocol used by early versions of RealPlayer */
320 
321 static int
322 alias_pna_out(struct ip *pip,
323 		  struct alias_link *link,
324 		  char *data,
325 		  int dlen)
326 {
327     struct alias_link *pna_links;
328     u_short msg_id, msg_len;
329     char    *work;
330     u_short alias_port, port;
331     struct  tcphdr *tc;
332 
333     work = data;
334     work += 5;
335     while (work + 4 < data + dlen) {
336 	memcpy(&msg_id, work, 2);
337 	work += 2;
338 	memcpy(&msg_len, work, 2);
339 	work += 2;
340 	if (ntohs(msg_id) == 0) {
341 	    /* end of options */
342 	    return 0;
343 	}
344 	if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
345 	    memcpy(&port, work, 2);
346 	    pna_links = FindUdpTcpOut(pip->ip_src, GetDestAddress(link),
347 				      port, 0, IPPROTO_UDP, 1);
348 	    if (pna_links != NULL) {
349 #ifndef NO_FW_PUNCH
350 		/* Punch hole in firewall */
351 		PunchFWHole(pna_links);
352 #endif
353 		tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
354 		alias_port = GetAliasPort(pna_links);
355 		memcpy(work, &alias_port, 2);
356 
357 		/* Compute TCP checksum for revised packet */
358 		tc->th_sum = 0;
359 		tc->th_sum = TcpChecksum(pip);
360 	    }
361 	}
362 	work += ntohs(msg_len);
363     }
364 
365     return 0;
366 }
367 
368 void
369 AliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize)
370 {
371     int    hlen, tlen, dlen;
372     struct tcphdr *tc;
373     char   *data;
374     const  char *setup = "SETUP", *pna = "PNA", *str200 = "200";
375     const  char *okstr = "OK", *client_port_str = "client_port";
376     const  char *server_port_str = "server_port";
377     int    i, parseOk;
378 
379     tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
380     hlen = (pip->ip_hl + tc->th_off) << 2;
381     tlen = ntohs(pip->ip_len);
382     dlen = tlen - hlen;
383 
384     data = (char*)pip;
385     data += hlen;
386 
387     /* When aliasing a client, check for the SETUP request */
388     if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
389       (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
390 
391       if (dlen >= strlen(setup)) {
392         if (memcmp(data, setup, strlen(setup)) == 0) {
393 	    alias_rtsp_out(pip, link, data, client_port_str);
394 	    return;
395 	}
396       }
397       if (dlen >= strlen(pna)) {
398 	if (memcmp(data, pna, strlen(pna)) == 0) {
399 	    alias_pna_out(pip, link, data, dlen);
400 	}
401       }
402 
403     } else {
404 
405       /* When aliasing a server, check for the 200 reply
406          Accomodate varying number of blanks between 200 & OK */
407 
408       if (dlen >= strlen(str200)) {
409 
410         for (parseOk = 0, i = 0;
411              i <= dlen - strlen(str200);
412              i++) {
413           if (memcmp(&data[i], str200, strlen(str200)) == 0) {
414             parseOk = 1;
415             break;
416           }
417         }
418         if (parseOk) {
419 
420           i += strlen(str200);        /* skip string found */
421           while(data[i] == ' ')       /* skip blank(s) */
422 	    i++;
423 
424           if ((dlen - i) >= strlen(okstr)) {
425 
426             if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
427               alias_rtsp_out(pip, link, data, server_port_str);
428 
429           }
430         }
431       }
432     }
433 }
434