xref: /freebsd/sys/netinet/libalias/alias_smedia.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
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$
66  */
67 
68 /*
69    Alias_smedia.c is meant to contain the aliasing code for streaming media
70    protocols.  It performs special processing for RSTP sessions under TCP.
71    Specifically, when a SETUP request is sent by a client, or a 200 reply
72    is sent by a server, it is intercepted and modified.  The address is
73    changed to the gateway machine and an aliasing port is used.
74 
75    More specifically, the "client_port" configuration parameter is
76    parsed for SETUP requests.  The "server_port" configuration parameter is
77    parsed for 200 replies eminating from a server.  This is intended to handle
78    the unicast case.
79 
80    RTSP also allows a redirection of a stream to another client by using the
81    "destination" configuration parameter.  The destination config parm would
82    indicate a different IP address.  This function is NOT supported by the
83    RTSP translation code below.
84 
85    The RTSP multicast functions without any address translation intervention.
86 
87    For this routine to work, the SETUP/200 must fit entirely
88    into a single TCP packet.  This is typically the case, but exceptions
89    can easily be envisioned under the actual specifications.
90 
91    Probably the most troubling aspect of the approach taken here is
92    that the new SETUP/200 will typically be a different length, and
93    this causes a certain amount of bookkeeping to keep track of the
94    changes of sequence and acknowledgment numbers, since the client
95    machine is totally unaware of the modification to the TCP stream.
96 
97    Initial version:  May, 2000 (eds)
98 */
99 
100 #include <stdio.h>
101 #include <string.h>
102 #include <sys/types.h>
103 #include <netinet/in_systm.h>
104 #include <netinet/in.h>
105 #include <netinet/ip.h>
106 #include <netinet/tcp.h>
107 #include <netinet/udp.h>
108 
109 #include "alias_local.h"
110 
111 #define RTSP_CONTROL_PORT_NUMBER_1 554
112 #define RTSP_CONTROL_PORT_NUMBER_2 7070
113 #define RTSP_PORT_GROUP            2
114 
115 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
116 
117 static int
118 search_string(char *data, int dlen, const char *search_str)
119 {
120     int i, j, k;
121     int search_str_len;
122 
123     search_str_len = strlen(search_str);
124     for (i = 0; i < dlen - search_str_len; i++) {
125 	for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
126 	    if (data[j] != search_str[k] &&
127 		data[j] != search_str[k] - ('a' - 'A')) {
128 		break;
129 	    }
130 	    if (k == search_str_len - 1) {
131 		return j + 1;
132 	    }
133 	}
134     }
135     return -1;
136 }
137 
138 static int
139 alias_rtsp_out(struct ip *pip,
140 		   struct alias_link *link,
141 		   char *data,
142 		   const char *port_str)
143 {
144     int     hlen, tlen, dlen;
145     struct tcphdr *tc;
146     int     i, j, pos, state, port_dlen, new_dlen, delta;
147     u_short p[2], new_len;
148     u_short sport, eport, base_port;
149     u_short salias = 0, ealias = 0, base_alias = 0;
150     const char *transport_str = "transport:";
151     char    newdata[2048], *port_data, *port_newdata, stemp[80];
152     int     links_created = 0, pkt_updated = 0;
153     struct alias_link *rtsp_link = NULL;
154     struct in_addr null_addr;
155 
156     /* Calculate data length of TCP packet */
157     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
158     hlen = (pip->ip_hl + tc->th_off) << 2;
159     tlen = ntohs(pip->ip_len);
160     dlen = tlen - hlen;
161 
162     /* Find keyword, "Transport: " */
163     pos = search_string(data, dlen, transport_str);
164     if (pos < 0) {
165 	return -1;
166     }
167     port_data = data + pos;
168     port_dlen = dlen - pos;
169 
170     memcpy(newdata, data, pos);
171     port_newdata = newdata + pos;
172 
173     while (port_dlen > strlen(port_str)) {
174 	/* Find keyword, appropriate port string */
175 	pos = search_string(port_data, port_dlen, port_str);
176 	if (pos < 0) {
177 	    break;
178 	}
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 		  /* Find an even numbered port number base that
221 		     satisfies the contiguous number of ports we need  */
222 		  null_addr.s_addr = 0;
223 		  if (0 == (salias = FindNewPortGroup(null_addr,
224 	       			    FindAliasAddress(pip->ip_src),
225 				    sport, 0,
226 				    RTSP_PORT_GROUP,
227 				    IPPROTO_UDP, 1))) {
228 #ifdef DEBUG
229 		    fprintf(stderr,
230 		    "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
231 #endif
232 		  } else {
233 
234   		    base_alias = ntohs(salias);
235 		    for (j = 0; j < RTSP_PORT_GROUP; j++) {
236 		      /* Establish link to port found in RTSP packet */
237 		      rtsp_link = FindRtspOut(GetOriginalAddress(link), null_addr,
238                                 htons(base_port + j), htons(base_alias + j),
239                                 IPPROTO_UDP);
240 		      if (rtsp_link != NULL) {
241 #ifndef NO_FW_PUNCH
242 		        /* Punch hole in firewall */
243 		        PunchFWHole(rtsp_link);
244 #endif
245 		      } else {
246 #ifdef DEBUG
247 		        fprintf(stderr,
248 		        "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
249 #endif
250 		        break;
251 		      }
252 		    }
253 		  }
254                   ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
255 		}
256 
257 		if (salias && rtsp_link) {
258 
259 		  pkt_updated = 1;
260 
261 	          /* Copy into IP packet */
262 		  sprintf(stemp, "%d", ntohs(salias));
263 		  memcpy(port_newdata, stemp, strlen(stemp));
264 		  port_newdata += strlen(stemp);
265 
266 		  if (eport != 0) {
267 		    *port_newdata = '-';
268 		    port_newdata++;
269 
270 		    /* Copy into IP packet */
271 		    sprintf(stemp, "%d", ntohs(ealias));
272 		    memcpy(port_newdata, stemp, strlen(stemp));
273 		    port_newdata += strlen(stemp);
274 		  }
275 
276 	          *port_newdata = ';';
277 		  port_newdata++;
278 		}
279 		state++;
280 		break;
281 	    }
282 	    if (state > 3) {
283 		break;
284 	    }
285 	}
286 	port_data += i;
287 	port_dlen -= i;
288     }
289 
290     if (!pkt_updated)
291       return -1;
292 
293     memcpy (port_newdata, port_data, port_dlen);
294     port_newdata += port_dlen;
295     *port_newdata = '\0';
296 
297     /* Create new packet */
298     new_dlen = port_newdata - newdata;
299     memcpy (data, newdata, new_dlen);
300 
301     SetAckModified(link);
302     delta = GetDeltaSeqOut(pip, link);
303     AddSeq(pip, link, delta + new_dlen - dlen);
304 
305     new_len = htons(hlen + new_dlen);
306     DifferentialChecksum(&pip->ip_sum,
307 			 &new_len,
308 			 &pip->ip_len,
309 			 1);
310     pip->ip_len = new_len;
311 
312     tc->th_sum = 0;
313     tc->th_sum = TcpChecksum(pip);
314 
315     return 0;
316 }
317 
318 /* Support the protocol used by early versions of RealPlayer */
319 
320 static int
321 alias_pna_out(struct ip *pip,
322 		  struct alias_link *link,
323 		  char *data,
324 		  int dlen)
325 {
326     struct alias_link *pna_links;
327     u_short msg_id, msg_len;
328     char    *work;
329     u_short alias_port, port;
330     struct  tcphdr *tc;
331 
332     work = data;
333     work += 5;
334     while (work + 4 < data + dlen) {
335 	memcpy(&msg_id, work, 2);
336 	work += 2;
337 	memcpy(&msg_len, work, 2);
338 	work += 2;
339 	if (ntohs(msg_id) == 0) {
340 	    /* end of options */
341 	    return 0;
342 	}
343 	if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
344 	    memcpy(&port, work, 2);
345 	    pna_links = FindUdpTcpOut(pip->ip_src, GetDestAddress(link),
346 				      port, 0, IPPROTO_UDP, 1);
347 	    if (pna_links != NULL) {
348 #ifndef NO_FW_PUNCH
349 		/* Punch hole in firewall */
350 		PunchFWHole(pna_links);
351 #endif
352 		tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
353 		alias_port = GetAliasPort(pna_links);
354 		memcpy(work, &alias_port, 2);
355 
356 		/* Compute TCP checksum for revised packet */
357 		tc->th_sum = 0;
358 		tc->th_sum = TcpChecksum(pip);
359 	    }
360 	}
361 	work += ntohs(msg_len);
362     }
363 
364     return 0;
365 }
366 
367 void
368 AliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize)
369 {
370     int    hlen, tlen, dlen;
371     struct tcphdr *tc;
372     char   *data;
373     const  char *setup = "SETUP", *pna = "PNA", *str200 = "200";
374     const  char *okstr = "OK", *client_port_str = "client_port";
375     const  char *server_port_str = "server_port";
376     int    i, parseOk;
377 
378     tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
379     hlen = (pip->ip_hl + tc->th_off) << 2;
380     tlen = ntohs(pip->ip_len);
381     dlen = tlen - hlen;
382 
383     data = (char*)pip;
384     data += hlen;
385 
386     /* When aliasing a client, check for the SETUP request */
387     if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
388       (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
389 
390       if (dlen >= strlen(setup)) {
391         if (memcmp(data, setup, strlen(setup)) == 0) {
392 	    alias_rtsp_out(pip, link, data, client_port_str);
393 	    return;
394 	}
395       }
396       if (dlen >= strlen(pna)) {
397 	if (memcmp(data, pna, strlen(pna)) == 0) {
398 	    alias_pna_out(pip, link, data, dlen);
399 	}
400       }
401 
402     } else {
403 
404       /* When aliasing a server, check for the 200 reply
405          Accomodate varying number of blanks between 200 & OK */
406 
407       if (dlen >= strlen(str200)) {
408 
409         for (parseOk = 0, i = 0;
410              i <= dlen - strlen(str200);
411              i++) {
412           if (memcmp(&data[i], str200, strlen(str200)) == 0) {
413             parseOk = 1;
414             break;
415           }
416         }
417         if (parseOk) {
418 
419           i += strlen(str200);        /* skip string found */
420           while(data[i] == ' ')       /* skip blank(s) */
421 	    i++;
422 
423           if ((dlen - i) >= strlen(okstr)) {
424 
425             if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
426               alias_rtsp_out(pip, link, data, server_port_str);
427 
428           }
429         }
430       }
431     }
432 }
433