Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*	$FreeBSD$	*/

/*
 * Copyright (C) 2000-2003 by Darren Reed
 *
 * See the IPFILTER.LICENCE file for details on licencing.
 *
 * Simple DCE transparent proxy for MSN RPC.
 *
 * ******* NOTE: THIS PROXY DOES NOT DO ADDRESS TRANSLATION ********
 *
 * Id: ip_msnrpc_pxy.c,v 2.17.2.1 2005/02/04 10:22:55 darrenr Exp
 */

#define	IPF_MSNRPC_PROXY

#define	IPF_MINMSNRPCLEN	24
#define	IPF_MSNRPCSKIP		(2 + 19 + 2 + 2 + 2 + 19 + 2 + 2)


typedef	struct	msnrpchdr	{
	u_char	mrh_major;	/* major # == 5 */
	u_char	mrh_minor;	/* minor # == 0 */
	u_char	mrh_type;
	u_char	mrh_flags;
	u_32_t	mrh_endian;
	u_short	mrh_dlen;	/* data size */
	u_short	mrh_alen;	/* authentication length */
	u_32_t	mrh_cid;	/* call identifier */
	u_32_t	mrh_hint;	/* allocation hint */
	u_short	mrh_ctxt;	/* presentation context hint */
	u_char	mrh_ccnt;	/* cancel count */
	u_char	mrh_ans;
} msnrpchdr_t;

int ippr_msnrpc_init __P((void));
void ippr_msnrpc_fini __P((void));
int ippr_msnrpc_new __P((fr_info_t *, ap_session_t *, nat_t *));
int ippr_msnrpc_out __P((fr_info_t *, ap_session_t *, nat_t *));
int ippr_msnrpc_in __P((fr_info_t *, ap_session_t *, nat_t *));
int ippr_msnrpc_check __P((ip_t *, msnrpchdr_t *));

static	frentry_t	msnfr;

int	msn_proxy_init = 0;

/*
 * Initialize local structures.
 */
int ippr_msnrpc_init()
{
	bzero((char *)&msnfr, sizeof(msnfr));
	msnfr.fr_ref = 1;
	msnfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
	MUTEX_INIT(&msnfr.fr_lock, "MSN RPC proxy rule lock");
	msn_proxy_init = 1;

	return 0;
}


void ippr_msnrpc_fini()
{
	if (msn_proxy_init == 1) {
		MUTEX_DESTROY(&msnfr.fr_lock);
		msn_proxy_init = 0;
	}
}


int ippr_msnrpc_new(fin, aps, nat)
fr_info_t *fin;
ap_session_t *aps;
nat_t *nat;
{
	msnrpcinfo_t *mri;

	KMALLOC(mri, msnrpcinfo_t *);
	if (mri == NULL)
		return -1;
	aps->aps_data = mri;
	aps->aps_psiz = sizeof(msnrpcinfo_t);

	bzero((char *)mri, sizeof(*mri));
	mri->mri_cmd[0] = 0xff;
	mri->mri_cmd[1] = 0xff;
	return 0;
}


int ippr_msnrpc_check(ip, mrh)
ip_t *ip;
msnrpchdr_t *mrh;
{
	if (mrh->mrh_major != 5)
		return -1;
	if (mrh->mrh_minor != 0)
		return -1;
	if (mrh->mrh_alen != 0)
		return -1;
	if (mrh->mrh_endian == 0x10) {
		/* Both gateway and packet match endian */
		if (mrh->mrh_dlen > ip->ip_len)
			return -1;
		if (mrh->mrh_type == 0 || mrh->mrh_type == 2)
			if (mrh->mrh_hint > ip->ip_len)
				return -1;
	} else if (mrh->mrh_endian == 0x10000000) {
		/* XXX - Endian mismatch - should be swapping! */
		return -1;
	} else {
		return -1;
	}
	return 0;
}


int ippr_msnrpc_out(fin, ip, aps, nat)
fr_info_t *fin;
ip_t *ip;
ap_session_t *aps;
nat_t *nat;
{
	msnrpcinfo_t *mri;
	msnrpchdr_t *mrh;
	tcphdr_t *tcp;
	int dlen;

	mri = aps->aps_data;
	if (mri == NULL)
		return 0;

	tcp = (tcphdr_t *)fin->fin_dp;
	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
	if (dlen < IPF_MINMSNRPCLEN)
		return 0;

	mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2));
	if (ippr_msnrpc_check(ip, mrh))
		return 0;

	mri->mri_valid++;

	switch (mrh->mrh_type)
	{
	case 0x0b :	/* BIND */
	case 0x00 :	/* REQUEST */
		break;
	case 0x0c :	/* BIND ACK */
	case 0x02 :	/* RESPONSE */
	default:
		return 0;
	}
	mri->mri_cmd[1] = mrh->mrh_type;
	return 0;
}


int ippr_msnrpc_in(fin, ip, aps, nat)
fr_info_t *fin;
ip_t *ip;
ap_session_t *aps;
nat_t *nat;
{
	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
	int dlen, sz, sz2, i;
	msnrpcinfo_t *mri;
	msnrpchdr_t *mrh;
	fr_info_t fi;
	u_short len;
	char *s;

	mri = aps->aps_data;
	if (mri == NULL)
		return 0;
	tcp = (tcphdr_t *)fin->fin_dp;
	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
	if (dlen < IPF_MINMSNRPCLEN)
		return 0;

	mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2));
	if (ippr_msnrpc_check(ip, mrh))
		return 0;

	mri->mri_valid++;

	switch (mrh->mrh_type)
	{
	case 0x0c :	/* BIND ACK */
		if (mri->mri_cmd[1] != 0x0b)
			return 0;
		break;
	case 0x02 :	/* RESPONSE */
		if (mri->mri_cmd[1] != 0x00)
			return 0;
		break;
	case 0x0b :	/* BIND */
	case 0x00 :	/* REQUEST */
	default:
		return 0;
	}
	mri->mri_cmd[0] = mrh->mrh_type;
	dlen -= sizeof(*mrh);

	/*
	 * Only processes RESPONSE's
	 */
	if (mrh->mrh_type != 0x02)
		return 0;

	/*
	 * Skip over some bytes...what are these really ?
	 */
	if (dlen <= 44)
		return 0;
	s = (char *)(mrh + 1) + 20;
	dlen -= 20;
	bcopy(s, (char *)&len, sizeof(len));
	if (len == 1) {
		s += 20;
		dlen -= 20;
	} else if (len == 2) {
		s += 24;
		dlen -= 24;
	} else
		return 0;

	if (dlen <= 10)
		return 0;
	dlen -= 10;
	bcopy(s, (char *)&sz, sizeof(sz));
	s += sizeof(sz);
	bcopy(s, (char *)&sz2, sizeof(sz2));
	s += sizeof(sz2);
	if (sz2 != sz)
		return 0;
	if (sz > dlen)
		return 0;
	if (*s++ != 5)
		return 0;
	if (*s++ != 0)
		return 0;
	sz -= IPF_MSNRPCSKIP;
	s += IPF_MSNRPCSKIP;
	dlen -= IPF_MSNRPCSKIP;

	do {
		if (sz < 7 || dlen < 7)
			break;
		bcopy(s, (char *)&len, sizeof(len));
		if (dlen < len)
			break;
		if (sz < len)
			break;

		if (len != 1)
			break;
		sz -= 3;
		i = *(s + 2);
		s += 3;
		dlen -= 3;

		bcopy(s, (char *)&len, sizeof(len));
		if (dlen < len)
			break;
		if (sz < len)
			break;
		s += sizeof(len);

		switch (i)
		{
		case 7 :
			if (len == 2) {
				bcopy(s, (char *)&mri->mri_rport, 2);
				mri->mri_flags |= 1;
			}
			break;
		case 9 :
			if (len == 4) {
				bcopy(s, (char *)&mri->mri_raddr, 4);
				mri->mri_flags |= 2;
			}
			break;
		default :
			break;
		}
		sz -= len;
		s += len;
		dlen -= len;
	} while (sz > 0);

	if (mri->mri_flags == 3) {
		int slen;

		bcopy((char *)fin, (char *)&fi, sizeof(fi));
		bzero((char *)tcp2, sizeof(*tcp2));

		slen = ip->ip_len;
		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
		bcopy((char *)fin, (char *)&fi, sizeof(fi));
		bzero((char *)tcp2, sizeof(*tcp2));
		tcp2->th_win = htons(8192);
		TCP_OFF_A(tcp2, 5);
		fi.fin_data[0] = htons(mri->mri_rport);
		tcp2->th_sport = mri->mri_rport;
		fi.fin_data[1] = 0;
		tcp2->th_dport = 0;
		fi.fin_state = NULL;
		fi.fin_nat = NULL;
		fi.fin_dlen = sizeof(*tcp2);
		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
		fi.fin_dp = (char *)tcp2;
		fi.fin_fi.fi_daddr = ip->ip_dst.s_addr;
		fi.fin_fi.fi_saddr = mri->mri_raddr.s_addr;
		if (!fi.fin_fr)
			fi.fin_fr = &msnfr;
		if (fr_stlookup(&fi, NULL, NULL)) {
			RWLOCK_EXIT(&ipf_state);
		} else {
			(void) fr_addstate(&fi, NULL, SI_W_DPORT|SI_CLONE);
			if (fi.fin_state != NULL)
				fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
		}
		ip->ip_len = slen;
	}
	mri->mri_flags = 0;
	return 0;
}