1145522Sdarrenr/*	$FreeBSD$	*/
2145522Sdarrenr
357126Sguido/*
457126Sguido * $FreeBSD$
5145522Sdarrenr * Copyright (C) 1998-2003 by Darren Reed
6145522Sdarrenr *
7145522Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
8145522Sdarrenr *
9170268Sdarrenr * $Id: ip_raudio_pxy.c,v 1.40.2.4 2006/07/14 06:12:17 darrenr Exp $
1057126Sguido */
1153642Sguido
1253642Sguido#define	IPF_RAUDIO_PROXY
1353642Sguido
1453642Sguido
1553642Sguidoint ippr_raudio_init __P((void));
16145522Sdarrenrvoid ippr_raudio_fini __P((void));
17145522Sdarrenrint ippr_raudio_new __P((fr_info_t *, ap_session_t *, nat_t *));
18145522Sdarrenrint ippr_raudio_in __P((fr_info_t *, ap_session_t *, nat_t *));
19145522Sdarrenrint ippr_raudio_out __P((fr_info_t *, ap_session_t *, nat_t *));
2053642Sguido
2153642Sguidostatic	frentry_t	raudiofr;
2253642Sguido
23145522Sdarrenrint	raudio_proxy_init = 0;
2453642Sguido
25145522Sdarrenr
2653642Sguido/*
2753642Sguido * Real Audio application proxy initialization.
2853642Sguido */
2953642Sguidoint ippr_raudio_init()
3053642Sguido{
3153642Sguido	bzero((char *)&raudiofr, sizeof(raudiofr));
3253642Sguido	raudiofr.fr_ref = 1;
3353642Sguido	raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
34145522Sdarrenr	MUTEX_INIT(&raudiofr.fr_lock, "Real Audio proxy rule lock");
35145522Sdarrenr	raudio_proxy_init = 1;
36145522Sdarrenr
3753642Sguido	return 0;
3853642Sguido}
3953642Sguido
4053642Sguido
41145522Sdarrenrvoid ippr_raudio_fini()
42145522Sdarrenr{
43145522Sdarrenr	if (raudio_proxy_init == 1) {
44145522Sdarrenr		MUTEX_DESTROY(&raudiofr.fr_lock);
45145522Sdarrenr		raudio_proxy_init = 0;
46145522Sdarrenr	}
47145522Sdarrenr}
48145522Sdarrenr
49145522Sdarrenr
5053642Sguido/*
5153642Sguido * Setup for a new proxy to handle Real Audio.
5253642Sguido */
53145522Sdarrenrint ippr_raudio_new(fin, aps, nat)
5453642Sguidofr_info_t *fin;
5553642Sguidoap_session_t *aps;
5653642Sguidonat_t *nat;
5753642Sguido{
5853642Sguido	raudio_t *rap;
5953642Sguido
6053642Sguido	KMALLOCS(aps->aps_data, void *, sizeof(raudio_t));
6160857Sdarrenr	if (aps->aps_data == NULL)
6260857Sdarrenr		return -1;
6360857Sdarrenr
64145522Sdarrenr	fin = fin;	/* LINT */
65145522Sdarrenr	nat = nat;	/* LINT */
66145522Sdarrenr
6760857Sdarrenr	bzero(aps->aps_data, sizeof(raudio_t));
6860857Sdarrenr	rap = aps->aps_data;
6960857Sdarrenr	aps->aps_psiz = sizeof(raudio_t);
7060857Sdarrenr	rap->rap_mode = RAP_M_TCP;	/* default is for TCP */
7153642Sguido	return 0;
7253642Sguido}
7353642Sguido
7453642Sguido
7553642Sguido
76145522Sdarrenrint ippr_raudio_out(fin, aps, nat)
7753642Sguidofr_info_t *fin;
7853642Sguidoap_session_t *aps;
7953642Sguidonat_t *nat;
8053642Sguido{
8153642Sguido	raudio_t *rap = aps->aps_data;
8255929Sguido	unsigned char membuf[512 + 1], *s;
8355929Sguido	u_short id = 0;
84145522Sdarrenr	tcphdr_t *tcp;
8580482Sdarrenr	int off, dlen;
8653642Sguido	int len = 0;
8753642Sguido	mb_t *m;
8853642Sguido
89145522Sdarrenr	nat = nat;	/* LINT */
90145522Sdarrenr
9153642Sguido	/*
9253642Sguido	 * If we've already processed the start messages, then nothing left
9353642Sguido	 * for the proxy to do.
9453642Sguido	 */
9553642Sguido	if (rap->rap_eos == 1)
9653642Sguido		return 0;
9753642Sguido
98145522Sdarrenr	m = fin->fin_m;
9953642Sguido	tcp = (tcphdr_t *)fin->fin_dp;
100145522Sdarrenr	off = (char *)tcp - (char *)fin->fin_ip;
101145522Sdarrenr	off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
10253642Sguido
103145522Sdarrenr#ifdef __sgi
104145522Sdarrenr	dlen = fin->fin_plen - off;
105145522Sdarrenr#else
106145522Sdarrenr	dlen = MSGDSIZE(m) - off;
107145522Sdarrenr#endif
10853642Sguido	if (dlen <= 0)
10953642Sguido		return 0;
11053642Sguido
111145522Sdarrenr	if (dlen > sizeof(membuf))
112145522Sdarrenr		dlen = sizeof(membuf);
113145522Sdarrenr
114145522Sdarrenr	bzero((char *)membuf, sizeof(membuf));
115145522Sdarrenr	COPYDATA(m, off, dlen, (char *)membuf);
11653642Sguido	/*
11753642Sguido	 * In all the startup parsing, ensure that we don't go outside
11853642Sguido	 * the packet buffer boundary.
11953642Sguido	 */
12053642Sguido	/*
12153642Sguido	 * Look for the start of connection "PNA" string if not seen yet.
12253642Sguido	 */
12353642Sguido	if (rap->rap_seenpna == 0) {
12455929Sguido		s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen);
12553642Sguido		if (s == NULL)
12653642Sguido			return 0;
12753642Sguido		s += 3;
12853642Sguido		rap->rap_seenpna = 1;
12953642Sguido	} else
13053642Sguido		s = membuf;
13153642Sguido
13253642Sguido	/*
13353642Sguido	 * Directly after the PNA will be the version number of this
13453642Sguido	 * connection.
13553642Sguido	 */
13653642Sguido	if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) {
13753642Sguido		if ((s + 1) - membuf < dlen) {
13853642Sguido			rap->rap_version = (*s << 8) | *(s + 1);
13953642Sguido			s += 2;
14053642Sguido			rap->rap_seenver = 1;
14153642Sguido		} else
14253642Sguido			return 0;
14353642Sguido	}
14453642Sguido
14553642Sguido	/*
14653642Sguido	 * Now that we've been past the PNA and version number, we're into the
14753642Sguido	 * startup messages block.  This ends when a message with an ID of 0.
14853642Sguido	 */
14953642Sguido	while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) {
15053642Sguido		if (rap->rap_gotid == 0) {
15153642Sguido			id = (*s << 8) | *(s + 1);
15253642Sguido			s += 2;
15353642Sguido			rap->rap_gotid = 1;
15453642Sguido			if (id == RA_ID_END) {
15553642Sguido				rap->rap_eos = 1;
15653642Sguido				break;
15753642Sguido			}
15853642Sguido		} else if (rap->rap_gotlen == 0) {
15953642Sguido			len = (*s << 8) | *(s + 1);
16053642Sguido			s += 2;
16153642Sguido			rap->rap_gotlen = 1;
16253642Sguido		}
16353642Sguido
16453642Sguido		if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) {
16553642Sguido			if (id == RA_ID_UDP) {
16653642Sguido				rap->rap_mode &= ~RAP_M_TCP;
16753642Sguido				rap->rap_mode |= RAP_M_UDP;
16853642Sguido				rap->rap_plport = (*s << 8) | *(s + 1);
16953642Sguido			} else if (id == RA_ID_ROBUST) {
17053642Sguido				rap->rap_mode |= RAP_M_ROBUST;
17153642Sguido				rap->rap_prport = (*s << 8) | *(s + 1);
17253642Sguido			}
17353642Sguido			s += len;
17453642Sguido			rap->rap_gotlen = 0;
17553642Sguido			rap->rap_gotid = 0;
17653642Sguido		}
17753642Sguido	}
17855929Sguido	return 0;
17953642Sguido}
18053642Sguido
18153642Sguido
182145522Sdarrenrint ippr_raudio_in(fin, aps, nat)
18353642Sguidofr_info_t *fin;
18453642Sguidoap_session_t *aps;
18553642Sguidonat_t *nat;
18653642Sguido{
18755929Sguido	unsigned char membuf[IPF_MAXPORTLEN + 1], *s;
18855929Sguido	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
18953642Sguido	raudio_t *rap = aps->aps_data;
19055929Sguido	struct in_addr swa, swb;
191145522Sdarrenr	int off, dlen, slen;
19267614Sdarrenr	int a1, a2, a3, a4;
19355929Sguido	u_short sp, dp;
19455929Sguido	fr_info_t fi;
19553642Sguido	tcp_seq seq;
196145522Sdarrenr	nat_t *nat2;
19755929Sguido	u_char swp;
198145522Sdarrenr	ip_t *ip;
19953642Sguido	mb_t *m;
20053642Sguido
20155929Sguido	/*
20255929Sguido	 * Wait until we've seen the end of the start messages and even then
20355929Sguido	 * only proceed further if we're using UDP.  If they want to use TCP
20455929Sguido	 * then data is sent back on the same channel that is already open.
20555929Sguido	 */
20655929Sguido	if (rap->rap_sdone != 0)
20753642Sguido		return 0;
20853642Sguido
209145522Sdarrenr	m = fin->fin_m;
21053642Sguido	tcp = (tcphdr_t *)fin->fin_dp;
211145522Sdarrenr	off = (char *)tcp - (char *)fin->fin_ip;
212145522Sdarrenr	off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
21353642Sguido
214145522Sdarrenr#ifdef __sgi
215145522Sdarrenr	dlen = fin->fin_plen - off;
21653642Sguido#else
217145522Sdarrenr	dlen = MSGDSIZE(m) - off;
218145522Sdarrenr#endif
21953642Sguido	if (dlen <= 0)
22053642Sguido		return 0;
22153642Sguido
222145522Sdarrenr	if (dlen > sizeof(membuf))
223145522Sdarrenr		dlen = sizeof(membuf);
224145522Sdarrenr
225145522Sdarrenr	bzero((char *)membuf, sizeof(membuf));
226145522Sdarrenr	COPYDATA(m, off, dlen, (char *)membuf);
227145522Sdarrenr
22853642Sguido	seq = ntohl(tcp->th_seq);
22953642Sguido	/*
23053642Sguido	 * Check to see if the data in this packet is of interest to us.
23153642Sguido	 * We only care for the first 19 bytes coming back from the server.
23253642Sguido	 */
23353642Sguido	if (rap->rap_sseq == 0) {
234145522Sdarrenr		s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen);
23553642Sguido		if (s == NULL)
23653642Sguido			return 0;
23753642Sguido		a1 = s - membuf;
23853642Sguido		dlen -= a1;
23953642Sguido		a1 = 0;
24053642Sguido		rap->rap_sseq = seq;
24153642Sguido		a2 = MIN(dlen, sizeof(rap->rap_svr));
24253642Sguido	} else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) {
24353642Sguido		/*
24453642Sguido		 * seq # which is the start of data and from that the offset
24553642Sguido		 * into the buffer array.
24653642Sguido		 */
24753642Sguido		a1 = seq - rap->rap_sseq;
24853642Sguido		a2 = MIN(dlen, sizeof(rap->rap_svr));
24953642Sguido		a2 -= a1;
25053642Sguido		s = membuf;
25153642Sguido	} else
25253642Sguido		return 0;
25353642Sguido
25455929Sguido	for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) {
25553642Sguido		rap->rap_sbf |= (1 << a3);
25653642Sguido		rap->rap_svr[a3] = *s++;
25753642Sguido	}
25855929Sguido
25955929Sguido	if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos))	/* 19 bits */
26055929Sguido		return 0;
26155929Sguido	rap->rap_sdone = 1;
26255929Sguido
26355929Sguido	s = (u_char *)rap->rap_svr + 11;
26455929Sguido	if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) {
26555929Sguido		s += 2;
26653642Sguido		rap->rap_srport = (*s << 8) | *(s + 1);
26753642Sguido	}
26855929Sguido
269145522Sdarrenr	ip = fin->fin_ip;
27055929Sguido	swp = ip->ip_p;
27155929Sguido	swa = ip->ip_src;
27255929Sguido	swb = ip->ip_dst;
27355929Sguido
27455929Sguido	ip->ip_p = IPPROTO_UDP;
27555929Sguido	ip->ip_src = nat->nat_inip;
27655929Sguido	ip->ip_dst = nat->nat_oip;
27755929Sguido
27855929Sguido	bcopy((char *)fin, (char *)&fi, sizeof(fi));
27955929Sguido	bzero((char *)tcp2, sizeof(*tcp2));
280145522Sdarrenr	TCP_OFF_A(tcp2, 5);
281145522Sdarrenr	fi.fin_state = NULL;
282145522Sdarrenr	fi.fin_nat = NULL;
283145522Sdarrenr	fi.fin_flx |= FI_IGNORE;
28455929Sguido	fi.fin_dp = (char *)tcp2;
28555929Sguido	fi.fin_fr = &raudiofr;
28667853Sdarrenr	fi.fin_dlen = sizeof(*tcp2);
287145522Sdarrenr	fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
28855929Sguido	tcp2->th_win = htons(8192);
28960857Sdarrenr	slen = ip->ip_len;
29060857Sdarrenr	ip->ip_len = fin->fin_hlen + sizeof(*tcp);
29155929Sguido
29255929Sguido	if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) &&
29355929Sguido	    (rap->rap_srport != 0)) {
29455929Sguido		dp = rap->rap_srport;
29555929Sguido		sp = rap->rap_prport;
29655929Sguido		tcp2->th_sport = htons(sp);
29755929Sguido		tcp2->th_dport = htons(dp);
29855929Sguido		fi.fin_data[0] = dp;
29955929Sguido		fi.fin_data[1] = sp;
30080482Sdarrenr		fi.fin_out = 0;
301145522Sdarrenr		nat2 = nat_new(&fi, nat->nat_ptr, NULL,
302145522Sdarrenr			       NAT_SLAVE|IPN_UDP | (sp ? 0 : SI_W_SPORT),
303145522Sdarrenr			       NAT_OUTBOUND);
304145522Sdarrenr		if (nat2 != NULL) {
305145522Sdarrenr			(void) nat_proto(&fi, nat2, IPN_UDP);
306145522Sdarrenr			nat_update(&fi, nat2, nat2->nat_ptr);
307145522Sdarrenr
308145522Sdarrenr			(void) fr_addstate(&fi, NULL, (sp ? 0 : SI_W_SPORT));
309145522Sdarrenr			if (fi.fin_state != NULL)
310170268Sdarrenr				fr_statederef((ipstate_t **)&fi.fin_state);
31155929Sguido		}
31255929Sguido	}
31355929Sguido
31455929Sguido	if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) {
31555929Sguido		sp = rap->rap_plport;
31655929Sguido		tcp2->th_sport = htons(sp);
31755929Sguido		tcp2->th_dport = 0; /* XXX - don't specify remote port */
31855929Sguido		fi.fin_data[0] = sp;
31955929Sguido		fi.fin_data[1] = 0;
32080482Sdarrenr		fi.fin_out = 1;
321145522Sdarrenr		nat2 = nat_new(&fi, nat->nat_ptr, NULL,
322145522Sdarrenr			       NAT_SLAVE|IPN_UDP|SI_W_DPORT,
323145522Sdarrenr			       NAT_OUTBOUND);
324145522Sdarrenr		if (nat2 != NULL) {
325145522Sdarrenr			(void) nat_proto(&fi, nat2, IPN_UDP);
326145522Sdarrenr			nat_update(&fi, nat2, nat2->nat_ptr);
327145522Sdarrenr
328145522Sdarrenr			(void) fr_addstate(&fi, NULL, SI_W_DPORT);
329145522Sdarrenr			if (fi.fin_state != NULL)
330170268Sdarrenr				fr_statederef((ipstate_t **)&fi.fin_state);
33155929Sguido		}
33255929Sguido	}
33360857Sdarrenr
33455929Sguido	ip->ip_p = swp;
33560857Sdarrenr	ip->ip_len = slen;
33655929Sguido	ip->ip_src = swa;
33755929Sguido	ip->ip_dst = swb;
33853642Sguido	return 0;
33953642Sguido}
340