ip_ftp_pxy.c revision 92685
153642Sguido/*
253642Sguido * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
353642Sguido * code.
472006Sdarrenr *
557126Sguido * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 92685 2002-03-19 11:44:16Z darrenr $
653642Sguido */
753642Sguido#if SOLARIS && defined(_KERNEL)
853642Sguidoextern	kmutex_t	ipf_rw;
953642Sguido#endif
1053642Sguido
1153642Sguido#define	isdigit(x)	((x) >= '0' && (x) <= '9')
1263523Sdarrenr#define	isupper(x)	(((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
1363523Sdarrenr#define	islower(x)	(((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
1463523Sdarrenr#define	isalpha(x)	(isupper(x) || islower(x))
1563523Sdarrenr#define	toupper(x)	(isupper(x) ? (x) : (x) - 'a' + 'A')
1653642Sguido
1753642Sguido#define	IPF_FTP_PROXY
1853642Sguido
1953642Sguido#define	IPF_MINPORTLEN	18
2053642Sguido#define	IPF_MAXPORTLEN	30
2153642Sguido#define	IPF_MIN227LEN	39
2253642Sguido#define	IPF_MAX227LEN	51
2360857Sdarrenr#define	IPF_FTPBUFSZ	96	/* This *MUST* be >= 53! */
2453642Sguido
2580482Sdarrenr#define	FTPXY_GO	0
2680482Sdarrenr#define	FTPXY_INIT	1
2780482Sdarrenr#define	FTPXY_USER_1	2
2880482Sdarrenr#define	FTPXY_USOK_1	3
2980482Sdarrenr#define	FTPXY_PASS_1	4
3080482Sdarrenr#define	FTPXY_PAOK_1	5
3180482Sdarrenr#define	FTPXY_AUTH_1	6
3280482Sdarrenr#define	FTPXY_AUOK_1	7
3380482Sdarrenr#define	FTPXY_ADAT_1	8
3480482Sdarrenr#define	FTPXY_ADOK_1	9
3580482Sdarrenr#define	FTPXY_ACCT_1	10
3680482Sdarrenr#define	FTPXY_ACOK_1	11
3780482Sdarrenr#define	FTPXY_USER_2	12
3880482Sdarrenr#define	FTPXY_USOK_2	13
3980482Sdarrenr#define	FTPXY_PASS_2	14
4080482Sdarrenr#define	FTPXY_PAOK_2	15
4153642Sguido
4260857Sdarrenrint ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
4360857Sdarrenrint ippr_ftp_complete __P((char *, size_t));
4460857Sdarrenrint ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
4553642Sguidoint ippr_ftp_init __P((void));
4660857Sdarrenrint ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
4753642Sguidoint ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
4860857Sdarrenrint ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
4960857Sdarrenrint ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
5060857Sdarrenrint ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
5160857Sdarrenrint ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
5292685Sdarrenrint ippr_ftp_valid __P((int, char *, size_t));
5392685Sdarrenrint ippr_ftp_server_valid __P((char *, size_t));
5492685Sdarrenrint ippr_ftp_client_valid __P((char *, size_t));
5560857Sdarrenru_short ippr_ftp_atoi __P((char **));
5653642Sguido
5792685Sdarrenrstatic	frentry_t	ftppxyfr;
5860857Sdarrenrint	ippr_ftp_pasvonly = 0;
5963523Sdarrenrint	ippr_ftp_insecure = 0;
6053642Sguido
6153642Sguido
6253642Sguido/*
6353642Sguido * Initialize local structures.
6453642Sguido */
6553642Sguidoint ippr_ftp_init()
6653642Sguido{
6792685Sdarrenr	bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
6892685Sdarrenr	ftppxyfr.fr_ref = 1;
6992685Sdarrenr	ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
7053642Sguido	return 0;
7153642Sguido}
7253642Sguido
7353642Sguido
7460857Sdarrenrint ippr_ftp_new(fin, ip, aps, nat)
7560857Sdarrenrfr_info_t *fin;
7660857Sdarrenrip_t *ip;
7760857Sdarrenrap_session_t *aps;
7860857Sdarrenrnat_t *nat;
7953642Sguido{
8060857Sdarrenr	ftpinfo_t *ftp;
8160857Sdarrenr	ftpside_t *f;
8253642Sguido
8360857Sdarrenr	KMALLOC(ftp, ftpinfo_t *);
8460857Sdarrenr	if (ftp == NULL)
8560857Sdarrenr		return -1;
8660857Sdarrenr	aps->aps_data = ftp;
8760857Sdarrenr	aps->aps_psiz = sizeof(ftpinfo_t);
8860857Sdarrenr
8960857Sdarrenr	bzero((char *)ftp, sizeof(*ftp));
9060857Sdarrenr	f = &ftp->ftp_side[0];
9160857Sdarrenr	f->ftps_rptr = f->ftps_buf;
9260857Sdarrenr	f->ftps_wptr = f->ftps_buf;
9360857Sdarrenr	f = &ftp->ftp_side[1];
9460857Sdarrenr	f->ftps_rptr = f->ftps_buf;
9560857Sdarrenr	f->ftps_wptr = f->ftps_buf;
9680482Sdarrenr	ftp->ftp_passok = FTPXY_INIT;
9760857Sdarrenr	return 0;
9853642Sguido}
9953642Sguido
10053642Sguido
10160857Sdarrenrint ippr_ftp_port(fin, ip, nat, f, dlen)
10253642Sguidofr_info_t *fin;
10353642Sguidoip_t *ip;
10453642Sguidonat_t *nat;
10560857Sdarrenrftpside_t *f;
10660857Sdarrenrint dlen;
10753642Sguido{
10853642Sguido	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
10960857Sdarrenr	char newbuf[IPF_FTPBUFSZ], *s;
11053642Sguido	u_int a1, a2, a3, a4;
11153642Sguido	struct in_addr swip;
11292685Sdarrenr	u_short a5, a6, sp;
11360857Sdarrenr	size_t nlen, olen;
11453642Sguido	fr_info_t fi;
11560857Sdarrenr	int inc, off;
11653642Sguido	nat_t *ipn;
11753642Sguido	mb_t *m;
11853642Sguido#if	SOLARIS
11953642Sguido	mb_t *m1;
12053642Sguido#endif
12153642Sguido
12253642Sguido	tcp = (tcphdr_t *)fin->fin_dp;
12360857Sdarrenr	/*
12460857Sdarrenr	 * Check for client sending out PORT message.
12560857Sdarrenr	 */
12660857Sdarrenr	if (dlen < IPF_MINPORTLEN)
12753642Sguido		return 0;
12863523Sdarrenr	off = fin->fin_hlen + (tcp->th_off << 2);
12960857Sdarrenr	/*
13053642Sguido	 * Skip the PORT command + space
13153642Sguido	 */
13260857Sdarrenr	s = f->ftps_rptr + 5;
13353642Sguido	/*
13453642Sguido	 * Pick out the address components, two at a time.
13553642Sguido	 */
13660857Sdarrenr	a1 = ippr_ftp_atoi(&s);
13753642Sguido	if (!s)
13853642Sguido		return 0;
13960857Sdarrenr	a2 = ippr_ftp_atoi(&s);
14053642Sguido	if (!s)
14153642Sguido		return 0;
14253642Sguido	/*
14353642Sguido	 * check that IP address in the PORT/PASV reply is the same as the
14453642Sguido	 * sender of the command - prevents using PORT for port scanning.
14553642Sguido	 */
14653642Sguido	a1 <<= 16;
14753642Sguido	a1 |= a2;
14853642Sguido	if (a1 != ntohl(nat->nat_inip.s_addr))
14953642Sguido		return 0;
15053642Sguido
15160857Sdarrenr	a5 = ippr_ftp_atoi(&s);
15253642Sguido	if (!s)
15353642Sguido		return 0;
15453642Sguido	if (*s == ')')
15553642Sguido		s++;
15653642Sguido
15753642Sguido	/*
15853642Sguido	 * check for CR-LF at the end.
15953642Sguido	 */
16053642Sguido	if (*s == '\n')
16153642Sguido		s--;
16253642Sguido	if ((*s == '\r') && (*(s + 1) == '\n')) {
16353642Sguido		s += 2;
16453642Sguido		a6 = a5 & 0xff;
16553642Sguido	} else
16653642Sguido		return 0;
16753642Sguido	a5 >>= 8;
16867614Sdarrenr	a5 &= 0xff;
16953642Sguido	/*
17053642Sguido	 * Calculate new address parts for PORT command
17153642Sguido	 */
17253642Sguido	a1 = ntohl(ip->ip_src.s_addr);
17353642Sguido	a2 = (a1 >> 16) & 0xff;
17453642Sguido	a3 = (a1 >> 8) & 0xff;
17553642Sguido	a4 = a1 & 0xff;
17653642Sguido	a1 >>= 24;
17760857Sdarrenr	olen = s - f->ftps_rptr;
17892685Sdarrenr	/* DO NOT change this to snprintf! */
17955929Sguido	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
18053642Sguido		       "PORT", a1, a2, a3, a4, a5, a6);
18153642Sguido
18253642Sguido	nlen = strlen(newbuf);
18353642Sguido	inc = nlen - olen;
18460857Sdarrenr	if ((inc + ip->ip_len) > 65535)
18560857Sdarrenr		return 0;
18660857Sdarrenr
18753642Sguido#if SOLARIS
18860857Sdarrenr	m = fin->fin_qfm;
18953642Sguido	for (m1 = m; m1->b_cont; m1 = m1->b_cont)
19053642Sguido		;
19153642Sguido	if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
19253642Sguido		mblk_t *nm;
19353642Sguido
19453642Sguido		/* alloc enough to keep same trailer space for lower driver */
19553642Sguido		nm = allocb(nlen, BPRI_MED);
19653642Sguido		PANIC((!nm),("ippr_ftp_out: allocb failed"));
19753642Sguido
19853642Sguido		nm->b_band = m1->b_band;
19953642Sguido		nm->b_wptr += nlen;
20053642Sguido
20153642Sguido		m1->b_wptr -= olen;
20253642Sguido		PANIC((m1->b_wptr < m1->b_rptr),
20353642Sguido		      ("ippr_ftp_out: cannot handle fragmented data block"));
20453642Sguido
20553642Sguido		linkb(m1, nm);
20653642Sguido	} else {
20753642Sguido		if (m1->b_datap->db_struiolim == m1->b_wptr)
20853642Sguido			m1->b_datap->db_struiolim += inc;
20953642Sguido		m1->b_datap->db_struioflag &= ~STRUIO_IP;
21053642Sguido		m1->b_wptr += inc;
21153642Sguido	}
21253642Sguido	copyin_mblk(m, off, nlen, newbuf);
21353642Sguido#else
21460857Sdarrenr	m = *((mb_t **)fin->fin_mp);
21553642Sguido	if (inc < 0)
21653642Sguido		m_adj(m, inc);
21753642Sguido	/* the mbuf chain will be extended if necessary by m_copyback() */
21853642Sguido	m_copyback(m, off, nlen, newbuf);
21963523Sdarrenr# ifdef	M_PKTHDR
22063523Sdarrenr	if (!(m->m_flags & M_PKTHDR))
22163523Sdarrenr		m->m_pkthdr.len += inc;
22263523Sdarrenr# endif
22353642Sguido#endif
22453642Sguido	if (inc != 0) {
22553642Sguido#if SOLARIS || defined(__sgi)
22653642Sguido		register u_32_t	sum1, sum2;
22753642Sguido
22853642Sguido		sum1 = ip->ip_len;
22953642Sguido		sum2 = ip->ip_len + inc;
23053642Sguido
23153642Sguido		/* Because ~1 == -2, We really need ~1 == -1 */
23253642Sguido		if (sum1 > sum2)
23353642Sguido			sum2--;
23453642Sguido		sum2 -= sum1;
23553642Sguido		sum2 = (sum2 & 0xffff) + (sum2 >> 16);
23653642Sguido
23780482Sdarrenr		fix_outcksum(fin, &ip->ip_sum, sum2);
23853642Sguido#endif
23953642Sguido		ip->ip_len += inc;
24053642Sguido	}
24153642Sguido
24253642Sguido	/*
24353642Sguido	 * Add skeleton NAT entry for connection which will come back the
24453642Sguido	 * other way.
24553642Sguido	 */
24692685Sdarrenr	sp = (a5 << 8 | a6);
24753642Sguido	/*
24860857Sdarrenr	 * Don't allow the PORT command to specify a port < 1024 due to
24960857Sdarrenr	 * security crap.
25060857Sdarrenr	 */
25192685Sdarrenr	if (sp < 1024)
25260857Sdarrenr		return 0;
25360857Sdarrenr	/*
25453642Sguido	 * The server may not make the connection back from port 20, but
25553642Sguido	 * it is the most likely so use it here to check for a conflicting
25653642Sguido	 * mapping.
25753642Sguido	 */
25892685Sdarrenr	bcopy((char *)fin, (char *)&fi, sizeof(fi));
25992685Sdarrenr	fi.fin_data[0] = sp;
26092685Sdarrenr	fi.fin_data[1] = fin->fin_data[1] - 1;
26192685Sdarrenr	ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
26292685Sdarrenr			    ip->ip_dst, 0);
26353642Sguido	if (ipn == NULL) {
26460857Sdarrenr		int slen;
26560857Sdarrenr
26660857Sdarrenr		slen = ip->ip_len;
26760857Sdarrenr		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
26853642Sguido		bzero((char *)tcp2, sizeof(*tcp2));
26953642Sguido		tcp2->th_win = htons(8192);
27092685Sdarrenr		tcp2->th_sport = htons(sp);
27160857Sdarrenr		tcp2->th_off = 5;
27253642Sguido		tcp2->th_dport = 0; /* XXX - don't specify remote port */
27353642Sguido		fi.fin_data[1] = 0;
27467853Sdarrenr		fi.fin_dlen = sizeof(*tcp2);
27553642Sguido		fi.fin_dp = (char *)tcp2;
27692685Sdarrenr		fi.fin_fr = &ftppxyfr;
27780482Sdarrenr		fi.fin_out = 1;
27853642Sguido		swip = ip->ip_src;
27972006Sdarrenr		fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
28053642Sguido		ip->ip_src = nat->nat_inip;
28192685Sdarrenr		ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_DPORT,
28253642Sguido			      NAT_OUTBOUND);
28353642Sguido		if (ipn != NULL) {
28453642Sguido			ipn->nat_age = fr_defnatage;
28592685Sdarrenr			(void) fr_addstate(ip, &fi, NULL,
28692685Sdarrenr					   FI_W_DPORT|FI_IGNOREPKT);
28753642Sguido		}
28860857Sdarrenr		ip->ip_len = slen;
28953642Sguido		ip->ip_src = swip;
29053642Sguido	}
29164580Sdarrenr	return APR_INC(inc);
29253642Sguido}
29353642Sguido
29453642Sguido
29560857Sdarrenrint ippr_ftp_client(fin, ip, nat, ftp, dlen)
29653642Sguidofr_info_t *fin;
29760857Sdarrenrnat_t *nat;
29860857Sdarrenrftpinfo_t *ftp;
29953642Sguidoip_t *ip;
30060857Sdarrenrint dlen;
30153642Sguido{
30263523Sdarrenr	char *rptr, *wptr, cmd[6], c;
30360857Sdarrenr	ftpside_t *f;
30463523Sdarrenr	int inc, i;
30560857Sdarrenr
30660857Sdarrenr	inc = 0;
30760857Sdarrenr	f = &ftp->ftp_side[0];
30860857Sdarrenr	rptr = f->ftps_rptr;
30960857Sdarrenr	wptr = f->ftps_wptr;
31060857Sdarrenr
31163523Sdarrenr	for (i = 0; (i < 5) && (i < dlen); i++) {
31263523Sdarrenr		c = rptr[i];
31363523Sdarrenr		if (isalpha(c)) {
31463523Sdarrenr			cmd[i] = toupper(c);
31563523Sdarrenr		} else {
31663523Sdarrenr			cmd[i] = c;
31763523Sdarrenr		}
31863523Sdarrenr	}
31963523Sdarrenr	cmd[i] = '\0';
32063523Sdarrenr
32180482Sdarrenr	ftp->ftp_incok = 0;
32280482Sdarrenr	if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
32380482Sdarrenr		if (ftp->ftp_passok == FTPXY_ADOK_1 ||
32480482Sdarrenr		    ftp->ftp_passok == FTPXY_AUOK_1) {
32580482Sdarrenr			ftp->ftp_passok = FTPXY_USER_2;
32680482Sdarrenr			ftp->ftp_incok = 1;
32780482Sdarrenr		} else {
32880482Sdarrenr			ftp->ftp_passok = FTPXY_USER_1;
32980482Sdarrenr			ftp->ftp_incok = 1;
33080482Sdarrenr		}
33180482Sdarrenr	} else if (!strncmp(cmd, "AUTH ", 5)) {
33280482Sdarrenr		ftp->ftp_passok = FTPXY_AUTH_1;
33380482Sdarrenr		ftp->ftp_incok = 1;
33480482Sdarrenr	} else if (!strncmp(cmd, "PASS ", 5)) {
33580482Sdarrenr		if (ftp->ftp_passok == FTPXY_USOK_1) {
33680482Sdarrenr			ftp->ftp_passok = FTPXY_PASS_1;
33780482Sdarrenr			ftp->ftp_incok = 1;
33880482Sdarrenr		} else if (ftp->ftp_passok == FTPXY_USOK_2) {
33980482Sdarrenr			ftp->ftp_passok = FTPXY_PASS_2;
34080482Sdarrenr			ftp->ftp_incok = 1;
34180482Sdarrenr		}
34280482Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
34380482Sdarrenr		   !strncmp(cmd, "ADAT ", 5)) {
34480482Sdarrenr		ftp->ftp_passok = FTPXY_ADAT_1;
34580482Sdarrenr		ftp->ftp_incok = 1;
34692685Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
34792685Sdarrenr		    ftp->ftp_passok == FTPXY_PAOK_2) &&
34880482Sdarrenr		 !strncmp(cmd, "ACCT ", 5)) {
34980482Sdarrenr		ftp->ftp_passok = FTPXY_ACCT_1;
35080482Sdarrenr		ftp->ftp_incok = 1;
35180482Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly &&
35263523Sdarrenr		 !strncmp(cmd, "PORT ", 5)) {
35360857Sdarrenr		inc = ippr_ftp_port(fin, ip, nat, f, dlen);
35463523Sdarrenr	} else if (ippr_ftp_insecure && !ippr_ftp_pasvonly &&
35563523Sdarrenr		   !strncmp(cmd, "PORT ", 5)) {
35663523Sdarrenr		inc = ippr_ftp_port(fin, ip, nat, f, dlen);
35760857Sdarrenr	}
35860857Sdarrenr
35960857Sdarrenr	while ((*rptr++ != '\n') && (rptr < wptr))
36060857Sdarrenr		;
36160857Sdarrenr	f->ftps_rptr = rptr;
36260857Sdarrenr	return inc;
36353642Sguido}
36453642Sguido
36553642Sguido
36660857Sdarrenrint ippr_ftp_pasv(fin, ip, nat, f, dlen)
36753642Sguidofr_info_t *fin;
36853642Sguidoip_t *ip;
36953642Sguidonat_t *nat;
37060857Sdarrenrftpside_t *f;
37160857Sdarrenrint dlen;
37253642Sguido{
37360857Sdarrenr	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
37453642Sguido	struct in_addr swip, swip2;
37553642Sguido	u_int a1, a2, a3, a4;
37692685Sdarrenr	u_short a5, a6, dp;
37753642Sguido	fr_info_t fi;
37853642Sguido	nat_t *ipn;
37963523Sdarrenr	int inc;
38060857Sdarrenr	char *s;
38153642Sguido
38280482Sdarrenr#define	PASV_REPLEN	24
38360857Sdarrenr	/*
38460857Sdarrenr	 * Check for PASV reply message.
38560857Sdarrenr	 */
38660857Sdarrenr	if (dlen < IPF_MIN227LEN)
38760857Sdarrenr		return 0;
38880482Sdarrenr	else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN))
38960857Sdarrenr		return 0;
39060857Sdarrenr
39153642Sguido	tcp = (tcphdr_t *)fin->fin_dp;
39253642Sguido
39353642Sguido	/*
39453642Sguido	 * Skip the PORT command + space
39553642Sguido	 */
39680482Sdarrenr	s = f->ftps_rptr + PASV_REPLEN;
39753642Sguido	while (*s && !isdigit(*s))
39853642Sguido		s++;
39953642Sguido	/*
40053642Sguido	 * Pick out the address components, two at a time.
40153642Sguido	 */
40260857Sdarrenr	a1 = ippr_ftp_atoi(&s);
40353642Sguido	if (!s)
40453642Sguido		return 0;
40560857Sdarrenr	a2 = ippr_ftp_atoi(&s);
40653642Sguido	if (!s)
40753642Sguido		return 0;
40853642Sguido
40953642Sguido	/*
41053642Sguido	 * check that IP address in the PORT/PASV reply is the same as the
41153642Sguido	 * sender of the command - prevents using PORT for port scanning.
41253642Sguido	 */
41353642Sguido	a1 <<= 16;
41453642Sguido	a1 |= a2;
41553642Sguido	if (a1 != ntohl(nat->nat_oip.s_addr))
41653642Sguido		return 0;
41753642Sguido
41860857Sdarrenr	a5 = ippr_ftp_atoi(&s);
41953642Sguido	if (!s)
42053642Sguido		return 0;
42153642Sguido
42253642Sguido	if (*s == ')')
42353642Sguido		s++;
42480482Sdarrenr	if (*s == '.')
42580482Sdarrenr		s++;
42653642Sguido	if (*s == '\n')
42753642Sguido		s--;
42853642Sguido	/*
42953642Sguido	 * check for CR-LF at the end.
43053642Sguido	 */
43153642Sguido	if ((*s == '\r') && (*(s + 1) == '\n')) {
43253642Sguido		s += 2;
43353642Sguido		a6 = a5 & 0xff;
43453642Sguido	} else
43553642Sguido		return 0;
43653642Sguido	a5 >>= 8;
43753642Sguido	/*
43853642Sguido	 * Calculate new address parts for 227 reply
43953642Sguido	 */
44053642Sguido	a1 = ntohl(ip->ip_src.s_addr);
44153642Sguido	a2 = (a1 >> 16) & 0xff;
44253642Sguido	a3 = (a1 >> 8) & 0xff;
44353642Sguido	a4 = a1 & 0xff;
44453642Sguido	a1 >>= 24;
44560857Sdarrenr	inc = 0;
44660857Sdarrenr#if 0
44760857Sdarrenr	olen = s - f->ftps_rptr;
44853642Sguido	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
44953642Sguido		       "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
45053642Sguido	nlen = strlen(newbuf);
45153642Sguido	inc = nlen - olen;
45260857Sdarrenr	if ((inc + ip->ip_len) > 65535)
45360857Sdarrenr		return 0;
45460857Sdarrenr
45553642Sguido#if SOLARIS
45660857Sdarrenr	m = fin->fin_qfm;
45753642Sguido	for (m1 = m; m1->b_cont; m1 = m1->b_cont)
45853642Sguido		;
45953642Sguido	if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
46053642Sguido		mblk_t *nm;
46153642Sguido
46253642Sguido		/* alloc enough to keep same trailer space for lower driver */
46353642Sguido		nm = allocb(nlen, BPRI_MED);
46453642Sguido		PANIC((!nm),("ippr_ftp_out: allocb failed"));
46553642Sguido
46653642Sguido		nm->b_band = m1->b_band;
46753642Sguido		nm->b_wptr += nlen;
46853642Sguido
46953642Sguido		m1->b_wptr -= olen;
47053642Sguido		PANIC((m1->b_wptr < m1->b_rptr),
47153642Sguido		      ("ippr_ftp_out: cannot handle fragmented data block"));
47253642Sguido
47353642Sguido		linkb(m1, nm);
47453642Sguido	} else {
47553642Sguido		m1->b_wptr += inc;
47653642Sguido	}
47760857Sdarrenr	/*copyin_mblk(m, off, nlen, newbuf);*/
47863523Sdarrenr#else /* SOLARIS */
47960857Sdarrenr	m = *((mb_t **)fin->fin_mp);
48053642Sguido	if (inc < 0)
48153642Sguido		m_adj(m, inc);
48253642Sguido	/* the mbuf chain will be extended if necessary by m_copyback() */
48360857Sdarrenr	/*m_copyback(m, off, nlen, newbuf);*/
48463523Sdarrenr#endif /* SOLARIS */
48553642Sguido	if (inc != 0) {
48653642Sguido#if SOLARIS || defined(__sgi)
48753642Sguido		register u_32_t	sum1, sum2;
48853642Sguido
48953642Sguido		sum1 = ip->ip_len;
49053642Sguido		sum2 = ip->ip_len + inc;
49153642Sguido
49253642Sguido		/* Because ~1 == -2, We really need ~1 == -1 */
49353642Sguido		if (sum1 > sum2)
49453642Sguido			sum2--;
49553642Sguido		sum2 -= sum1;
49653642Sguido		sum2 = (sum2 & 0xffff) + (sum2 >> 16);
49753642Sguido
49880482Sdarrenr		fix_outcksum(fin, &ip->ip_sum, sum2);
49963523Sdarrenr#endif /* SOLARIS || defined(__sgi) */
50053642Sguido		ip->ip_len += inc;
50153642Sguido	}
50263523Sdarrenr#endif /* 0 */
50353642Sguido
50453642Sguido	/*
50553642Sguido	 * Add skeleton NAT entry for connection which will come back the
50653642Sguido	 * other way.
50753642Sguido	 */
50892685Sdarrenr	bcopy((char *)fin, (char *)&fi, sizeof(fi));
50992685Sdarrenr	fi.fin_data[0] = 0;
51053642Sguido	dp = htons(fin->fin_data[1] - 1);
51192685Sdarrenr	fi.fin_data[1] = ntohs(dp);
51292685Sdarrenr	ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
51392685Sdarrenr			    ip->ip_dst, 0);
51453642Sguido	if (ipn == NULL) {
51560857Sdarrenr		int slen;
51660857Sdarrenr
51760857Sdarrenr		slen = ip->ip_len;
51860857Sdarrenr		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
51953642Sguido		bzero((char *)tcp2, sizeof(*tcp2));
52053642Sguido		tcp2->th_win = htons(8192);
52153642Sguido		tcp2->th_sport = 0;		/* XXX - fake it for nat_new */
52260857Sdarrenr		tcp2->th_off = 5;
52392685Sdarrenr		fi.fin_data[1] = a5 << 8 | a6;
52467853Sdarrenr		fi.fin_dlen = sizeof(*tcp2);
52592685Sdarrenr		tcp2->th_dport = htons(fi.fin_data[1]);
52692685Sdarrenr		fi.fin_data[0] = 0;
52753642Sguido		fi.fin_dp = (char *)tcp2;
52892685Sdarrenr		fi.fin_fr = &ftppxyfr;
52980482Sdarrenr		fi.fin_out = 1;
53053642Sguido		swip = ip->ip_src;
53153642Sguido		swip2 = ip->ip_dst;
53272006Sdarrenr		fi.fin_fi.fi_daddr = ip->ip_src.s_addr;
53372006Sdarrenr		fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
53453642Sguido		ip->ip_dst = ip->ip_src;
53553642Sguido		ip->ip_src = nat->nat_inip;
53692685Sdarrenr		ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_SPORT,
53753642Sguido			      NAT_OUTBOUND);
53853642Sguido		if (ipn != NULL) {
53953642Sguido			ipn->nat_age = fr_defnatage;
54092685Sdarrenr			(void) fr_addstate(ip, &fi, NULL,
54192685Sdarrenr					   FI_W_SPORT|FI_IGNOREPKT);
54253642Sguido		}
54360857Sdarrenr		ip->ip_len = slen;
54453642Sguido		ip->ip_src = swip;
54553642Sguido		ip->ip_dst = swip2;
54653642Sguido	}
54753642Sguido	return inc;
54853642Sguido}
54953642Sguido
55053642Sguido
55160857Sdarrenrint ippr_ftp_server(fin, ip, nat, ftp, dlen)
55260857Sdarrenrfr_info_t *fin;
55360857Sdarrenrip_t *ip;
55460857Sdarrenrnat_t *nat;
55560857Sdarrenrftpinfo_t *ftp;
55660857Sdarrenrint dlen;
55760857Sdarrenr{
55860857Sdarrenr	char *rptr, *wptr;
55960857Sdarrenr	ftpside_t *f;
56060857Sdarrenr	int inc;
56160857Sdarrenr
56260857Sdarrenr	inc = 0;
56360857Sdarrenr	f = &ftp->ftp_side[1];
56460857Sdarrenr	rptr = f->ftps_rptr;
56560857Sdarrenr	wptr = f->ftps_wptr;
56660857Sdarrenr
56780482Sdarrenr	if (!isdigit(*rptr) || !isdigit(*(rptr + 1)) || !isdigit(*(rptr + 2)))
56880482Sdarrenr		return inc;
56980482Sdarrenr	if (ftp->ftp_passok == FTPXY_GO) {
57080482Sdarrenr		if (!strncmp(rptr, "227 ", 4))
57180482Sdarrenr			inc = ippr_ftp_pasv(fin, ip, nat, f, dlen);
57263523Sdarrenr	} else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
57363523Sdarrenr		inc = ippr_ftp_pasv(fin, ip, nat, f, dlen);
57480482Sdarrenr	} else if (*rptr == '5' || *rptr == '4')
57580482Sdarrenr		ftp->ftp_passok = FTPXY_INIT;
57680482Sdarrenr	else if (ftp->ftp_incok) {
57780482Sdarrenr		if (*rptr == '3') {
57880482Sdarrenr			if (ftp->ftp_passok == FTPXY_ACCT_1)
57980482Sdarrenr				ftp->ftp_passok = FTPXY_GO;
58080482Sdarrenr			else
58180482Sdarrenr				ftp->ftp_passok++;
58280482Sdarrenr		} else if (*rptr == '2') {
58380482Sdarrenr			switch (ftp->ftp_passok)
58480482Sdarrenr			{
58580482Sdarrenr			case FTPXY_USER_1 :
58680482Sdarrenr			case FTPXY_USER_2 :
58780482Sdarrenr			case FTPXY_PASS_1 :
58880482Sdarrenr			case FTPXY_PASS_2 :
58980482Sdarrenr			case FTPXY_ACCT_1 :
59080482Sdarrenr				ftp->ftp_passok = FTPXY_GO;
59180482Sdarrenr				break;
59280482Sdarrenr			default :
59380482Sdarrenr				ftp->ftp_passok += 3;
59480482Sdarrenr				break;
59580482Sdarrenr			}
59680482Sdarrenr		}
59760857Sdarrenr	}
59880482Sdarrenr	ftp->ftp_incok = 0;
59960857Sdarrenr	while ((*rptr++ != '\n') && (rptr < wptr))
60060857Sdarrenr		;
60160857Sdarrenr	f->ftps_rptr = rptr;
60260857Sdarrenr	return inc;
60360857Sdarrenr}
60460857Sdarrenr
60560857Sdarrenr
60660857Sdarrenr/*
60760857Sdarrenr * Look to see if the buffer starts with something which we recognise as
60860857Sdarrenr * being the correct syntax for the FTP protocol.
60960857Sdarrenr */
61092685Sdarrenrint ippr_ftp_client_valid(buf, len)
61160857Sdarrenrchar *buf;
61260857Sdarrenrsize_t len;
61360857Sdarrenr{
61460857Sdarrenr	register char *s, c;
61560857Sdarrenr	register size_t i = len;
61660857Sdarrenr
61760857Sdarrenr	if (i < 5)
61860857Sdarrenr		return 2;
61960857Sdarrenr	s = buf;
62060857Sdarrenr	c = *s++;
62160857Sdarrenr	i--;
62260857Sdarrenr
62392685Sdarrenr	if (isalpha(c)) {
62460857Sdarrenr		c = *s++;
62560857Sdarrenr		i--;
62692685Sdarrenr		if (isalpha(c)) {
62760857Sdarrenr			c = *s++;
62860857Sdarrenr			i--;
62992685Sdarrenr			if (isalpha(c)) {
63060857Sdarrenr				c = *s++;
63160857Sdarrenr				i--;
63292685Sdarrenr				if (isalpha(c)) {
63392685Sdarrenr					c = *s++;
63492685Sdarrenr					i--;
63592685Sdarrenr					if ((c != ' ') && (c != '\r'))
63692685Sdarrenr						return 1;
63792685Sdarrenr				} else if ((c != ' ') && (c != '\r'))
63860857Sdarrenr					return 1;
63960857Sdarrenr			} else
64060857Sdarrenr				return 1;
64160857Sdarrenr		} else
64260857Sdarrenr			return 1;
64392685Sdarrenr	} else
64492685Sdarrenr		return 1;
64592685Sdarrenr	for (; i; i--) {
64660857Sdarrenr		c = *s++;
64792685Sdarrenr		if (c == '\n')
64892685Sdarrenr			return 0;
64992685Sdarrenr	}
65092685Sdarrenr	return 2;
65192685Sdarrenr}
65292685Sdarrenr
65392685Sdarrenr
65492685Sdarrenrint ippr_ftp_server_valid(buf, len)
65592685Sdarrenrchar *buf;
65692685Sdarrenrsize_t len;
65792685Sdarrenr{
65892685Sdarrenr	register char *s, c;
65992685Sdarrenr	register size_t i = len;
66092685Sdarrenr
66192685Sdarrenr	if (i < 5)
66292685Sdarrenr		return 2;
66392685Sdarrenr	s = buf;
66492685Sdarrenr	c = *s++;
66592685Sdarrenr	i--;
66692685Sdarrenr
66792685Sdarrenr	if (isdigit(c)) {
66892685Sdarrenr		c = *s++;
66960857Sdarrenr		i--;
67092685Sdarrenr		if (isdigit(c)) {
67160857Sdarrenr			c = *s++;
67260857Sdarrenr			i--;
67392685Sdarrenr			if (isdigit(c)) {
67460857Sdarrenr				c = *s++;
67560857Sdarrenr				i--;
67692685Sdarrenr				if ((c != '-') && (c != ' '))
67760857Sdarrenr					return 1;
67860857Sdarrenr			} else
67960857Sdarrenr				return 1;
68060857Sdarrenr		} else
68160857Sdarrenr			return 1;
68260857Sdarrenr	} else
68360857Sdarrenr		return 1;
68460857Sdarrenr	for (; i; i--) {
68560857Sdarrenr		c = *s++;
68660857Sdarrenr		if (c == '\n')
68760857Sdarrenr			return 0;
68860857Sdarrenr	}
68960857Sdarrenr	return 2;
69060857Sdarrenr}
69160857Sdarrenr
69260857Sdarrenr
69392685Sdarrenrint ippr_ftp_valid(side, buf, len)
69492685Sdarrenrint side;
69592685Sdarrenrchar *buf;
69692685Sdarrenrsize_t len;
69792685Sdarrenr{
69892685Sdarrenr	int ret;
69992685Sdarrenr
70092685Sdarrenr	if (side == 0)
70192685Sdarrenr		ret = ippr_ftp_client_valid(buf, len);
70292685Sdarrenr	else
70392685Sdarrenr		ret = ippr_ftp_server_valid(buf, len);
70492685Sdarrenr	return ret;
70592685Sdarrenr}
70692685Sdarrenr
70792685Sdarrenr
70860857Sdarrenrint ippr_ftp_process(fin, ip, nat, ftp, rv)
70960857Sdarrenrfr_info_t *fin;
71060857Sdarrenrip_t *ip;
71160857Sdarrenrnat_t *nat;
71260857Sdarrenrftpinfo_t *ftp;
71360857Sdarrenrint rv;
71460857Sdarrenr{
71563523Sdarrenr	int mlen, len, off, inc, i, sel;
71660857Sdarrenr	char *rptr, *wptr;
71763523Sdarrenr	ftpside_t *f, *t;
71860857Sdarrenr	tcphdr_t *tcp;
71960857Sdarrenr	mb_t *m;
72060857Sdarrenr
72160857Sdarrenr	tcp = (tcphdr_t *)fin->fin_dp;
72260857Sdarrenr	off = fin->fin_hlen + (tcp->th_off << 2);
72360857Sdarrenr
72460857Sdarrenr#if	SOLARIS
72560857Sdarrenr	m = fin->fin_qfm;
72660857Sdarrenr#else
72760857Sdarrenr	m = *((mb_t **)fin->fin_mp);
72860857Sdarrenr#endif
72960857Sdarrenr
73060857Sdarrenr#if	SOLARIS
73160857Sdarrenr	mlen = msgdsize(m) - off;
73260857Sdarrenr#else
73360857Sdarrenr	mlen = mbufchainlen(m) - off;
73460857Sdarrenr#endif
73572006Sdarrenr
73663523Sdarrenr	t = &ftp->ftp_side[1 - rv];
73772006Sdarrenr	f = &ftp->ftp_side[rv];
73863523Sdarrenr	if (!mlen) {
73972006Sdarrenr		if (!t->ftps_seq ||
74072006Sdarrenr		    (int)ntohl(tcp->th_ack) - (int)t->ftps_seq > 0)
74172006Sdarrenr			t->ftps_seq = ntohl(tcp->th_ack);
74272006Sdarrenr		f->ftps_len = 0;
74360857Sdarrenr		return 0;
74463523Sdarrenr	}
74560857Sdarrenr
74660857Sdarrenr	inc = 0;
74760857Sdarrenr	rptr = f->ftps_rptr;
74860857Sdarrenr	wptr = f->ftps_wptr;
74960857Sdarrenr
75063523Sdarrenr	sel = nat->nat_aps->aps_sel[1 - rv];
75163523Sdarrenr	if (rv)
75263523Sdarrenr		i = nat->nat_aps->aps_ackoff[sel];
75363523Sdarrenr	else
75463523Sdarrenr		i = nat->nat_aps->aps_seqoff[sel];
75560857Sdarrenr	/*
75660857Sdarrenr	 * XXX - Ideally, this packet should get dropped because we now know
75760857Sdarrenr	 * that it is out of order (and there is no real danger in doing so
75860857Sdarrenr	 * apart from causing packets to go through here ordered).
75960857Sdarrenr	 */
76072006Sdarrenr	if (f->ftps_len + f->ftps_seq == ntohl(tcp->th_seq))
76172006Sdarrenr		f->ftps_seq = ntohl(tcp->th_seq);
76272006Sdarrenr	else if (ntohl(tcp->th_seq) + i != f->ftps_seq) {
76392685Sdarrenr		return APR_ERR(1);
76460857Sdarrenr	}
76572006Sdarrenr	f->ftps_len = mlen;
76660857Sdarrenr
76760857Sdarrenr	while (mlen > 0) {
76860857Sdarrenr		len = MIN(mlen, FTP_BUFSZ / 2);
76960857Sdarrenr
77060857Sdarrenr#if	SOLARIS
77160857Sdarrenr		copyout_mblk(m, off, len, wptr);
77260857Sdarrenr#else
77360857Sdarrenr		m_copydata(m, off, len, wptr);
77460857Sdarrenr#endif
77560857Sdarrenr		mlen -= len;
77660857Sdarrenr		off += len;
77760857Sdarrenr		wptr += len;
77860857Sdarrenr		f->ftps_wptr = wptr;
77960857Sdarrenr		if (f->ftps_junk == 2)
78092685Sdarrenr			f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr);
78160857Sdarrenr
78260857Sdarrenr		while ((f->ftps_junk == 0) && (wptr > rptr)) {
78392685Sdarrenr			f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr);
78460857Sdarrenr			if (f->ftps_junk == 0) {
78592685Sdarrenr				f->ftps_cmds++;
78660857Sdarrenr				len = wptr - rptr;
78760857Sdarrenr				f->ftps_rptr = rptr;
78860857Sdarrenr				if (rv)
78960857Sdarrenr					inc += ippr_ftp_server(fin, ip, nat,
79060857Sdarrenr							       ftp, len);
79160857Sdarrenr				else
79260857Sdarrenr					inc += ippr_ftp_client(fin, ip, nat,
79360857Sdarrenr							       ftp, len);
79460857Sdarrenr				rptr = f->ftps_rptr;
79592685Sdarrenr				wptr = f->ftps_wptr;
79660857Sdarrenr			}
79760857Sdarrenr		}
79860857Sdarrenr
79992685Sdarrenr		/*
80092685Sdarrenr		 * Off to a bad start so lets just forget about using the
80192685Sdarrenr		 * ftp proxy for this connection.
80292685Sdarrenr		 */
80392685Sdarrenr		if ((f->ftps_cmds == 0) && (f->ftps_junk == 1))
80492685Sdarrenr			return APR_ERR(2);
80592685Sdarrenr
80660857Sdarrenr		while ((f->ftps_junk == 1) && (rptr < wptr)) {
80760857Sdarrenr			while ((rptr < wptr) && (*rptr != '\r'))
80860857Sdarrenr				rptr++;
80960857Sdarrenr
81067614Sdarrenr			if (*rptr == '\r') {
81167614Sdarrenr				if (rptr + 1 < wptr) {
81267614Sdarrenr					if (*(rptr + 1) == '\n') {
81367614Sdarrenr						rptr += 2;
81467614Sdarrenr						f->ftps_junk = 0;
81567614Sdarrenr					} else
81667614Sdarrenr						rptr++;
81760857Sdarrenr				} else
81867614Sdarrenr					break;
81960857Sdarrenr			}
82060857Sdarrenr		}
82167614Sdarrenr		f->ftps_rptr = rptr;
82260857Sdarrenr
82360857Sdarrenr		if (rptr == wptr) {
82460857Sdarrenr			rptr = wptr = f->ftps_buf;
82560857Sdarrenr		} else {
82660857Sdarrenr			if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) {
82760857Sdarrenr				i = wptr - rptr;
82860857Sdarrenr				if ((rptr == f->ftps_buf) ||
82960857Sdarrenr				    (wptr - rptr > FTP_BUFSZ / 2)) {
83060857Sdarrenr					f->ftps_junk = 1;
83160857Sdarrenr					rptr = wptr = f->ftps_buf;
83260857Sdarrenr				} else {
83360857Sdarrenr					bcopy(rptr, f->ftps_buf, i);
83460857Sdarrenr					wptr = f->ftps_buf + i;
83560857Sdarrenr					rptr = f->ftps_buf;
83660857Sdarrenr				}
83760857Sdarrenr			}
83860857Sdarrenr			f->ftps_rptr = rptr;
83960857Sdarrenr			f->ftps_wptr = wptr;
84060857Sdarrenr		}
84160857Sdarrenr	}
84260857Sdarrenr
84363523Sdarrenr	t->ftps_seq = ntohl(tcp->th_ack);
84460857Sdarrenr	f->ftps_rptr = rptr;
84560857Sdarrenr	f->ftps_wptr = wptr;
84664580Sdarrenr	return APR_INC(inc);
84760857Sdarrenr}
84860857Sdarrenr
84960857Sdarrenr
85060857Sdarrenrint ippr_ftp_out(fin, ip, aps, nat)
85160857Sdarrenrfr_info_t *fin;
85260857Sdarrenrip_t *ip;
85360857Sdarrenrap_session_t *aps;
85460857Sdarrenrnat_t *nat;
85560857Sdarrenr{
85660857Sdarrenr	ftpinfo_t *ftp;
85760857Sdarrenr
85860857Sdarrenr	ftp = aps->aps_data;
85960857Sdarrenr	if (ftp == NULL)
86060857Sdarrenr		return 0;
86160857Sdarrenr	return ippr_ftp_process(fin, ip, nat, ftp, 0);
86260857Sdarrenr}
86360857Sdarrenr
86460857Sdarrenr
86553642Sguidoint ippr_ftp_in(fin, ip, aps, nat)
86653642Sguidofr_info_t *fin;
86753642Sguidoip_t *ip;
86853642Sguidoap_session_t *aps;
86953642Sguidonat_t *nat;
87053642Sguido{
87160857Sdarrenr	ftpinfo_t *ftp;
87253642Sguido
87360857Sdarrenr	ftp = aps->aps_data;
87460857Sdarrenr	if (ftp == NULL)
87560857Sdarrenr		return 0;
87660857Sdarrenr	return ippr_ftp_process(fin, ip, nat, ftp, 1);
87753642Sguido}
87860857Sdarrenr
87960857Sdarrenr
88060857Sdarrenr/*
88160857Sdarrenr * ippr_ftp_atoi - implement a version of atoi which processes numbers in
88260857Sdarrenr * pairs separated by commas (which are expected to be in the range 0 - 255),
88360857Sdarrenr * returning a 16 bit number combining either side of the , as the MSB and
88460857Sdarrenr * LSB.
88560857Sdarrenr */
88660857Sdarrenru_short ippr_ftp_atoi(ptr)
88760857Sdarrenrchar **ptr;
88860857Sdarrenr{
88960857Sdarrenr	register char *s = *ptr, c;
89060857Sdarrenr	register u_char i = 0, j = 0;
89160857Sdarrenr
89260857Sdarrenr	while ((c = *s++) && isdigit(c)) {
89360857Sdarrenr		i *= 10;
89460857Sdarrenr		i += c - '0';
89560857Sdarrenr	}
89660857Sdarrenr	if (c != ',') {
89760857Sdarrenr		*ptr = NULL;
89860857Sdarrenr		return 0;
89960857Sdarrenr	}
90060857Sdarrenr	while ((c = *s++) && isdigit(c)) {
90160857Sdarrenr		j *= 10;
90260857Sdarrenr		j += c - '0';
90360857Sdarrenr	}
90460857Sdarrenr	*ptr = s;
90567614Sdarrenr	i &= 0xff;
90667614Sdarrenr	j &= 0xff;
90760857Sdarrenr	return (i << 8) | j;
90860857Sdarrenr}
909