alias_smedia.c revision 63899
1331722Seadler/*
2156952Sume * alias_smedia.c
3156952Sume *
4156952Sume * Copyright (c) 2000 Whistle Communications, Inc.
5156952Sume * All rights reserved.
6156952Sume *
7156952Sume * Subject to the following obligations and disclaimer of warranty, use and
8156952Sume * redistribution of this software, in source or object code forms, with or
9156952Sume * without modifications are expressly permitted by Whistle Communications;
10156952Sume * provided, however, that:
11156952Sume * 1. Any and all reproductions of the source or object code must include the
12156952Sume *    copyright notice above and the following disclaimer of warranties; and
13156952Sume * 2. No rights are granted, in any manner or form, to use Whistle
14156952Sume *    Communications, Inc. trademarks, including the mark "WHISTLE
15156952Sume *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
16156952Sume *    such appears in the above copyright notice or in the software.
17156952Sume *
18156952Sume * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
19156952Sume * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
20156952Sume * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
21156952Sume * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
22156952Sume * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
23156952Sume * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
24156952Sume * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
25156952Sume * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
26156952Sume * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
27156952Sume * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
28156952Sume * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
29156952Sume * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
30156952Sume * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
31156952Sume * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32156952Sume * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33156952Sume * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
34156952Sume * OF SUCH DAMAGE.
35156952Sume *
36156952Sume * Copyright (c) 2000  Junichi SATOH <junichi@astec.co.jp>
37156952Sume *                                   <junichi@junichi.org>
38156952Sume * All rights reserved.
39156952Sume *
40156952Sume * Redistribution and use in source and binary forms, with or without
41156952Sume * modification, are permitted provided that the following conditions
42156952Sume * are met:
43156952Sume * 1. Redistributions of source code must retain the above copyright
44156952Sume *    notice, this list of conditions and the following disclaimer.
45156952Sume * 2. Redistributions in binary form must reproduce the above copyright
46156952Sume *    notice, this list of conditions and the following disclaimer in the
47156952Sume *    documentation and/or other materials provided with the distribution.
48156952Sume *
49269867Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
50156952Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51156956Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52156956Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
53156952Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54156952Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55156952Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56156956Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57156952Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58156952Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59156952Sume * SUCH DAMAGE.
60156952Sume *
61156952Sume * Authors: Erik Salander <erik@whistle.com>
62156952Sume *          Junichi SATOH <junichi@astec.co.jp>
63156952Sume *                        <junichi@junichi.org>
64156952Sume *
65156952Sume * $FreeBSD: head/sys/netinet/libalias/alias_smedia.c 63899 2000-07-26 23:15:46Z archie $
66156952Sume */
67156956Sume
68156952Sume/*
69156952Sume   Alias_smedia.c is meant to contain the aliasing code for streaming media
70156952Sume   protocols.  It performs special processing for RSTP sessions under TCP.
71156952Sume   Specifically, when a SETUP request is sent by a client, or a 200 reply
72156952Sume   is sent by a server, it is intercepted and modified.  The address is
73170244Sume   changed to the gateway machine and an aliasing port is used.
74170244Sume
75170244Sume   More specifically, the "client_port" configuration parameter is
76170244Sume   parsed for SETUP requests.  The "server_port" configuration parameter is
77156952Sume   parsed for 200 replies eminating from a server.  This is intended to handle
78298120Spfg   the unicast case.
79156952Sume
80156952Sume   RTSP also allows a redirection of a stream to another client by using the
81156952Sume   "destination" configuration parameter.  The destination config parm would
82156952Sume   indicate a different IP address.  This function is NOT supported by the
83170244Sume   RTSP translation code below.
84156952Sume
85156952Sume   The RTSP multicast functions without any address translation intervention.
86156952Sume
87156952Sume   For this routine to work, the SETUP/200 must fit entirely
88156952Sume   into a single TCP packet.  This is typically the case, but exceptions
89156952Sume   can easily be envisioned under the actual specifications.
90156952Sume
91156952Sume   Probably the most troubling aspect of the approach taken here is
92156952Sume   that the new SETUP/200 will typically be a different length, and
93156952Sume   this causes a certain amount of bookkeeping to keep track of the
94156952Sume   changes of sequence and acknowledgment numbers, since the client
95156952Sume   machine is totally unaware of the modification to the TCP stream.
96156952Sume
97156952Sume   Initial version:  May, 2000 (eds)
98156952Sume*/
99156952Sume
100156952Sume#include <stdio.h>
101156952Sume#include <string.h>
102156952Sume#include <sys/types.h>
103156952Sume#include <netinet/in_systm.h>
104156952Sume#include <netinet/in.h>
105156952Sume#include <netinet/ip.h>
106156952Sume#include <netinet/tcp.h>
107156952Sume#include <netinet/udp.h>
108156952Sume
109156956Sume#include "alias_local.h"
110156952Sume
111156952Sume#define RTSP_CONTROL_PORT_NUMBER_1 554
112170244Sume#define RTSP_CONTROL_PORT_NUMBER_2 7070
113156952Sume#define RTSP_PORT_GROUP            2
114156952Sume
115156952Sume#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
116156952Sume
117156952Sumeint search_string(char *data, int dlen, char *search_str)
118156952Sume{
119156952Sume    int i, j, k;
120156952Sume    int search_str_len;
121156952Sume
122156952Sume    search_str_len = strlen(search_str);
123156952Sume    for (i = 0; i < dlen - search_str_len; i++) {
124170244Sume	for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
125170244Sume	    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
137int 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
318int 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
364void
365AliasHandleRtspOut(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