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