17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (C) 1997-2003 by Darren Reed
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * See the IPFILTER.LICENCE file for details on licencing.
57c478bd9Sstevel@tonic-gate  *
6ab25eeb5Syz  * $Id: ip_ftp_pxy.c,v 2.88.2.15 2005/03/19 19:38:10 darrenr Exp $
7ab25eeb5Syz  *
8*f4b3ec61Sdh  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
97c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
107c478bd9Sstevel@tonic-gate  *
117c478bd9Sstevel@tonic-gate  * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
127c478bd9Sstevel@tonic-gate  * code.
13ab25eeb5Syz */
147c478bd9Sstevel@tonic-gate 
157c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
167c478bd9Sstevel@tonic-gate 
177c478bd9Sstevel@tonic-gate #define	IPF_FTP_PROXY
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate #define	IPF_MINPORTLEN	18
207c478bd9Sstevel@tonic-gate #define	IPF_MAXPORTLEN	30
217c478bd9Sstevel@tonic-gate #define	IPF_MIN227LEN	39
227c478bd9Sstevel@tonic-gate #define	IPF_MAX227LEN	51
237c478bd9Sstevel@tonic-gate #define	IPF_MIN229LEN	47
247c478bd9Sstevel@tonic-gate #define	IPF_MAX229LEN	51
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #define	FTPXY_GO	0
277c478bd9Sstevel@tonic-gate #define	FTPXY_INIT	1
287c478bd9Sstevel@tonic-gate #define	FTPXY_USER_1	2
297c478bd9Sstevel@tonic-gate #define	FTPXY_USOK_1	3
307c478bd9Sstevel@tonic-gate #define	FTPXY_PASS_1	4
317c478bd9Sstevel@tonic-gate #define	FTPXY_PAOK_1	5
327c478bd9Sstevel@tonic-gate #define	FTPXY_AUTH_1	6
337c478bd9Sstevel@tonic-gate #define	FTPXY_AUOK_1	7
347c478bd9Sstevel@tonic-gate #define	FTPXY_ADAT_1	8
357c478bd9Sstevel@tonic-gate #define	FTPXY_ADOK_1	9
367c478bd9Sstevel@tonic-gate #define	FTPXY_ACCT_1	10
377c478bd9Sstevel@tonic-gate #define	FTPXY_ACOK_1	11
387c478bd9Sstevel@tonic-gate #define	FTPXY_USER_2	12
397c478bd9Sstevel@tonic-gate #define	FTPXY_USOK_2	13
407c478bd9Sstevel@tonic-gate #define	FTPXY_PASS_2	14
417c478bd9Sstevel@tonic-gate #define	FTPXY_PAOK_2	15
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /*
447c478bd9Sstevel@tonic-gate  * Values for FTP commands.  Numerics cover 0-999
457c478bd9Sstevel@tonic-gate  */
467c478bd9Sstevel@tonic-gate #define	FTPXY_C_PASV	1000
477c478bd9Sstevel@tonic-gate 
48*f4b3ec61Sdh typedef struct ifs_ftppxy {
49*f4b3ec61Sdh 	frentry_t		ftppxyfr;
50*f4b3ec61Sdh 	int			ftp_proxy_init;
51*f4b3ec61Sdh 	int			ippr_ftp_pasvonly;
52*f4b3ec61Sdh 	int			ippr_ftp_insecure;
53*f4b3ec61Sdh 				/* Do not require logins before transfers */
54*f4b3ec61Sdh 	int			ippr_ftp_pasvrdr;
55*f4b3ec61Sdh 	int			ippr_ftp_forcepasv;
56*f4b3ec61Sdh 				/* PASV must be last command prior to 227 */
57*f4b3ec61Sdh 	/*
58*f4b3ec61Sdh 	 * 1 - security
59*f4b3ec61Sdh 	 * 2 - errors
60*f4b3ec61Sdh 	 * 3 - error debugging
61*f4b3ec61Sdh 	 * 4 - parsing errors
62*f4b3ec61Sdh 	 * 5 - parsing info
63*f4b3ec61Sdh 	 * 6 - parsing debug
64*f4b3ec61Sdh 	 */
65*f4b3ec61Sdh 	int			ippr_ftp_debug;
66*f4b3ec61Sdh 	ipftuneable_t		ftptune;
67*f4b3ec61Sdh } ifs_ftppxy_t;
68*f4b3ec61Sdh 
69*f4b3ec61Sdh int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
70*f4b3ec61Sdh     ifs_ftppxy_t *));
717c478bd9Sstevel@tonic-gate int ippr_ftp_complete __P((char *, size_t));
72*f4b3ec61Sdh int ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *, void *));
73*f4b3ec61Sdh int ippr_ftp_init __P((void **, ipf_stack_t *));
74*f4b3ec61Sdh void ippr_ftp_fini __P((void **, ipf_stack_t *));
75*f4b3ec61Sdh int ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *, void *));
76*f4b3ec61Sdh int ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *, void *));
77*f4b3ec61Sdh int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
78*f4b3ec61Sdh     ifs_ftppxy_t *));
79*f4b3ec61Sdh int ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int,
80*f4b3ec61Sdh     ifs_ftppxy_t *));
81*f4b3ec61Sdh int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int,
82*f4b3ec61Sdh     ifs_ftppxy_t *));
83*f4b3ec61Sdh int ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int,
84*f4b3ec61Sdh     ifs_ftppxy_t *));
85*f4b3ec61Sdh int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
86*f4b3ec61Sdh     ifs_ftppxy_t *));
87*f4b3ec61Sdh int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t, ifs_ftppxy_t *));
88*f4b3ec61Sdh int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t, ifs_ftppxy_t *));
89*f4b3ec61Sdh int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t, ifs_ftppxy_t *));
907c478bd9Sstevel@tonic-gate u_short ippr_ftp_atoi __P((char **));
917c478bd9Sstevel@tonic-gate int ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *,
92*f4b3ec61Sdh 			    u_int, char *, char *, u_int, ifs_ftppxy_t *));
93*f4b3ec61Sdh 
94*f4b3ec61Sdh /*
95*f4b3ec61Sdh  * Initialize local structures.
96*f4b3ec61Sdh  */
97*f4b3ec61Sdh int ippr_ftp_init(private, ifs)
98*f4b3ec61Sdh void **private;
99*f4b3ec61Sdh ipf_stack_t *ifs;
100*f4b3ec61Sdh {
101*f4b3ec61Sdh 	ifs_ftppxy_t *ifsftp;
1027c478bd9Sstevel@tonic-gate 
103*f4b3ec61Sdh 	KMALLOC(ifsftp, ifs_ftppxy_t *);
104*f4b3ec61Sdh 	if (ifsftp == NULL)
105*f4b3ec61Sdh 		return -1;
1067c478bd9Sstevel@tonic-gate 
107*f4b3ec61Sdh 	bzero((char *)&ifsftp->ftppxyfr, sizeof(ifsftp->ftppxyfr));
108*f4b3ec61Sdh 	ifsftp->ftppxyfr.fr_ref = 1;
109*f4b3ec61Sdh 	ifsftp->ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
110*f4b3ec61Sdh 	MUTEX_INIT(&ifsftp->ftppxyfr.fr_lock, "FTP Proxy Mutex");
111*f4b3ec61Sdh 	ifsftp->ftp_proxy_init = 1;
112*f4b3ec61Sdh 	ifsftp->ippr_ftp_pasvonly = 0;
113*f4b3ec61Sdh 	ifsftp->ippr_ftp_insecure = 0;
114*f4b3ec61Sdh 	ifsftp->ippr_ftp_pasvrdr = 0;
115*f4b3ec61Sdh 	ifsftp->ippr_ftp_forcepasv = 0;
116ab25eeb5Syz #if defined(_KERNEL)
117*f4b3ec61Sdh 	ifsftp->ippr_ftp_debug = 0;
118ab25eeb5Syz #else
119*f4b3ec61Sdh 	ifsftp->ippr_ftp_debug = 2;
120ab25eeb5Syz #endif
121*f4b3ec61Sdh 	bzero((char *)&ifsftp->ftptune, sizeof(ifsftp->ftptune));
122*f4b3ec61Sdh 	ifsftp->ftptune.ipft_pint = (uint_t *)&ifsftp->ippr_ftp_debug;
123*f4b3ec61Sdh 	ifsftp->ftptune.ipft_name = "ippr_ftp_debug";
124*f4b3ec61Sdh 	ifsftp->ftptune.ipft_max = 10;
125*f4b3ec61Sdh 	ifsftp->ftptune.ipft_sz = sizeof(ifsftp->ippr_ftp_debug);
126*f4b3ec61Sdh 	ifsftp->ftptune.ipft_next = NULL;
127ab25eeb5Syz 
128*f4b3ec61Sdh 	(void) fr_addipftune(&ifsftp->ftptune, ifs);
1297c478bd9Sstevel@tonic-gate 
130*f4b3ec61Sdh 	*private = (void *)ifsftp;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	return 0;
1337c478bd9Sstevel@tonic-gate }
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 
136*f4b3ec61Sdh void ippr_ftp_fini(private, ifs)
137*f4b3ec61Sdh void **private;
138*f4b3ec61Sdh ipf_stack_t *ifs;
1397c478bd9Sstevel@tonic-gate {
140*f4b3ec61Sdh 	ifs_ftppxy_t *ifsftp = *((ifs_ftppxy_t **)private);
141ab25eeb5Syz 
142*f4b3ec61Sdh 	(void) fr_delipftune(&ifsftp->ftptune, ifs);
143*f4b3ec61Sdh 
144*f4b3ec61Sdh 	if (ifsftp->ftp_proxy_init == 1) {
145*f4b3ec61Sdh 		MUTEX_DESTROY(&ifsftp->ftppxyfr.fr_lock);
146*f4b3ec61Sdh 		ifsftp->ftp_proxy_init = 0;
1477c478bd9Sstevel@tonic-gate 	}
148*f4b3ec61Sdh 
149*f4b3ec61Sdh 	KFREE(ifsftp);
150*f4b3ec61Sdh 	*private = NULL;
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 
154*f4b3ec61Sdh /*ARGSUSED*/
155*f4b3ec61Sdh int ippr_ftp_new(fin, aps, nat, private)
1567c478bd9Sstevel@tonic-gate fr_info_t *fin;
1577c478bd9Sstevel@tonic-gate ap_session_t *aps;
1587c478bd9Sstevel@tonic-gate nat_t *nat;
159*f4b3ec61Sdh void *private;
1607c478bd9Sstevel@tonic-gate {
1617c478bd9Sstevel@tonic-gate 	ftpinfo_t *ftp;
1627c478bd9Sstevel@tonic-gate 	ftpside_t *f;
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	KMALLOC(ftp, ftpinfo_t *);
1657c478bd9Sstevel@tonic-gate 	if (ftp == NULL)
1667c478bd9Sstevel@tonic-gate 		return -1;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	fin = fin;	/* LINT */
1697c478bd9Sstevel@tonic-gate 	nat = nat;	/* LINT */
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	aps->aps_data = ftp;
1727c478bd9Sstevel@tonic-gate 	aps->aps_psiz = sizeof(ftpinfo_t);
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	bzero((char *)ftp, sizeof(*ftp));
1757c478bd9Sstevel@tonic-gate 	f = &ftp->ftp_side[0];
1767c478bd9Sstevel@tonic-gate 	f->ftps_rptr = f->ftps_buf;
1777c478bd9Sstevel@tonic-gate 	f->ftps_wptr = f->ftps_buf;
1787c478bd9Sstevel@tonic-gate 	f = &ftp->ftp_side[1];
1797c478bd9Sstevel@tonic-gate 	f->ftps_rptr = f->ftps_buf;
1807c478bd9Sstevel@tonic-gate 	f->ftps_wptr = f->ftps_buf;
1817c478bd9Sstevel@tonic-gate 	ftp->ftp_passok = FTPXY_INIT;
1827c478bd9Sstevel@tonic-gate 	ftp->ftp_incok = 0;
1837c478bd9Sstevel@tonic-gate 	return 0;
1847c478bd9Sstevel@tonic-gate }
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 
187*f4b3ec61Sdh int ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp)
1887c478bd9Sstevel@tonic-gate fr_info_t *fin;
1897c478bd9Sstevel@tonic-gate ip_t *ip;
1907c478bd9Sstevel@tonic-gate nat_t *nat;
1917c478bd9Sstevel@tonic-gate ftpside_t *f;
1927c478bd9Sstevel@tonic-gate int dlen;
193*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate 	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
1967c478bd9Sstevel@tonic-gate 	char newbuf[IPF_FTPBUFSZ], *s;
1977c478bd9Sstevel@tonic-gate 	struct in_addr swip, swip2;
1987c478bd9Sstevel@tonic-gate 	u_int a1, a2, a3, a4;
199ab25eeb5Syz 	int inc, off, flags;
2007c478bd9Sstevel@tonic-gate 	u_short a5, a6, sp;
2017c478bd9Sstevel@tonic-gate 	size_t nlen, olen;
2027c478bd9Sstevel@tonic-gate 	fr_info_t fi;
2037c478bd9Sstevel@tonic-gate 	nat_t *nat2;
2047c478bd9Sstevel@tonic-gate 	mb_t *m;
205*f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	m = fin->fin_m;
2087c478bd9Sstevel@tonic-gate 	tcp = (tcphdr_t *)fin->fin_dp;
209ab25eeb5Syz 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	/*
2127c478bd9Sstevel@tonic-gate 	 * Check for client sending out PORT message.
2137c478bd9Sstevel@tonic-gate 	 */
2147c478bd9Sstevel@tonic-gate 	if (dlen < IPF_MINPORTLEN) {
215*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
216ab25eeb5Syz 			printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
217ab25eeb5Syz 			       dlen);
2187c478bd9Sstevel@tonic-gate 		return 0;
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 	/*
2217c478bd9Sstevel@tonic-gate 	 * Skip the PORT command + space
2227c478bd9Sstevel@tonic-gate 	 */
2237c478bd9Sstevel@tonic-gate 	s = f->ftps_rptr + 5;
2247c478bd9Sstevel@tonic-gate 	/*
2257c478bd9Sstevel@tonic-gate 	 * Pick out the address components, two at a time.
2267c478bd9Sstevel@tonic-gate 	 */
2277c478bd9Sstevel@tonic-gate 	a1 = ippr_ftp_atoi(&s);
2287c478bd9Sstevel@tonic-gate 	if (s == NULL) {
229*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
230ab25eeb5Syz 			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1);
2317c478bd9Sstevel@tonic-gate 		return 0;
2327c478bd9Sstevel@tonic-gate 	}
2337c478bd9Sstevel@tonic-gate 	a2 = ippr_ftp_atoi(&s);
2347c478bd9Sstevel@tonic-gate 	if (s == NULL) {
235*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
236ab25eeb5Syz 			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2);
2377c478bd9Sstevel@tonic-gate 		return 0;
2387c478bd9Sstevel@tonic-gate 	}
239ab25eeb5Syz 
2407c478bd9Sstevel@tonic-gate 	/*
2417c478bd9Sstevel@tonic-gate 	 * Check that IP address in the PORT/PASV reply is the same as the
2427c478bd9Sstevel@tonic-gate 	 * sender of the command - prevents using PORT for port scanning.
2437c478bd9Sstevel@tonic-gate 	 */
2447c478bd9Sstevel@tonic-gate 	a1 <<= 16;
2457c478bd9Sstevel@tonic-gate 	a1 |= a2;
2467c478bd9Sstevel@tonic-gate 	if (((nat->nat_dir == NAT_OUTBOUND) &&
2477c478bd9Sstevel@tonic-gate 	     (a1 != ntohl(nat->nat_inip.s_addr))) ||
2487c478bd9Sstevel@tonic-gate 	    ((nat->nat_dir == NAT_INBOUND) &&
2497c478bd9Sstevel@tonic-gate 	     (a1 != ntohl(nat->nat_oip.s_addr)))) {
250*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 0)
251ab25eeb5Syz 			printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1");
2527c478bd9Sstevel@tonic-gate 		return APR_ERR(1);
2537c478bd9Sstevel@tonic-gate 	}
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	a5 = ippr_ftp_atoi(&s);
2567c478bd9Sstevel@tonic-gate 	if (s == NULL) {
257*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
258ab25eeb5Syz 			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3);
2597c478bd9Sstevel@tonic-gate 		return 0;
2607c478bd9Sstevel@tonic-gate 	}
2617c478bd9Sstevel@tonic-gate 	if (*s == ')')
2627c478bd9Sstevel@tonic-gate 		s++;
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	/*
2657c478bd9Sstevel@tonic-gate 	 * check for CR-LF at the end.
2667c478bd9Sstevel@tonic-gate 	 */
2677c478bd9Sstevel@tonic-gate 	if (*s == '\n')
2687c478bd9Sstevel@tonic-gate 		s--;
2697c478bd9Sstevel@tonic-gate 	if ((*s == '\r') && (*(s + 1) == '\n')) {
2707c478bd9Sstevel@tonic-gate 		s += 2;
2717c478bd9Sstevel@tonic-gate 		a6 = a5 & 0xff;
2727c478bd9Sstevel@tonic-gate 	} else {
273*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
274ab25eeb5Syz 			printf("ippr_ftp_port:missing %s\n", "cr-lf");
2757c478bd9Sstevel@tonic-gate 		return 0;
2767c478bd9Sstevel@tonic-gate 	}
277ab25eeb5Syz 
2787c478bd9Sstevel@tonic-gate 	a5 >>= 8;
2797c478bd9Sstevel@tonic-gate 	a5 &= 0xff;
280ab25eeb5Syz 	sp = a5 << 8 | a6;
281ab25eeb5Syz 	/*
282ab25eeb5Syz 	 * Don't allow the PORT command to specify a port < 1024 due to
283ab25eeb5Syz 	 * security crap.
284ab25eeb5Syz 	 */
285ab25eeb5Syz 	if (sp < 1024) {
286*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 0)
287ab25eeb5Syz 			printf("ippr_ftp_port:sp(%d) < 1024\n", sp);
288ab25eeb5Syz 		return 0;
289ab25eeb5Syz 	}
2907c478bd9Sstevel@tonic-gate 	/*
2917c478bd9Sstevel@tonic-gate 	 * Calculate new address parts for PORT command
2927c478bd9Sstevel@tonic-gate 	 */
2937c478bd9Sstevel@tonic-gate 	if (nat->nat_dir == NAT_INBOUND)
2947c478bd9Sstevel@tonic-gate 		a1 = ntohl(nat->nat_oip.s_addr);
2957c478bd9Sstevel@tonic-gate 	else
2967c478bd9Sstevel@tonic-gate 		a1 = ntohl(ip->ip_src.s_addr);
2977c478bd9Sstevel@tonic-gate 	a2 = (a1 >> 16) & 0xff;
2987c478bd9Sstevel@tonic-gate 	a3 = (a1 >> 8) & 0xff;
2997c478bd9Sstevel@tonic-gate 	a4 = a1 & 0xff;
3007c478bd9Sstevel@tonic-gate 	a1 >>= 24;
3017c478bd9Sstevel@tonic-gate 	olen = s - f->ftps_rptr;
3027c478bd9Sstevel@tonic-gate 	/* DO NOT change this to snprintf! */
303ab25eeb5Syz #if defined(SNPRINTF) && defined(_KERNEL)
304ab25eeb5Syz 	(void) SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
305ab25eeb5Syz 		 "PORT", a1, a2, a3, a4, a5, a6);
3067c478bd9Sstevel@tonic-gate #else
3077c478bd9Sstevel@tonic-gate 	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
3087c478bd9Sstevel@tonic-gate 		       "PORT", a1, a2, a3, a4, a5, a6);
3097c478bd9Sstevel@tonic-gate #endif
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	nlen = strlen(newbuf);
3127c478bd9Sstevel@tonic-gate 	inc = nlen - olen;
3137c478bd9Sstevel@tonic-gate 	if ((inc + ip->ip_len) > 65535) {
314*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 0)
315ab25eeb5Syz 			printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n",
316ab25eeb5Syz 			       inc);
3177c478bd9Sstevel@tonic-gate 		return 0;
3187c478bd9Sstevel@tonic-gate 	}
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate #if !defined(_KERNEL)
3217c478bd9Sstevel@tonic-gate 	bcopy(newbuf, MTOD(m, char *) + off, nlen);
3227c478bd9Sstevel@tonic-gate #else
3237c478bd9Sstevel@tonic-gate # if defined(MENTAT)
3247c478bd9Sstevel@tonic-gate 	if (inc < 0)
3257c478bd9Sstevel@tonic-gate 		(void)adjmsg(m, inc);
326ab25eeb5Syz # else /* defined(MENTAT) */
327ab25eeb5Syz 	/*
328ab25eeb5Syz 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
329ab25eeb5Syz 	 * mean remove -len bytes from the end of the packet.
330ab25eeb5Syz 	 * The mbuf chain will be extended if necessary by m_copyback().
331ab25eeb5Syz 	 */
3327c478bd9Sstevel@tonic-gate 	if (inc < 0)
3337c478bd9Sstevel@tonic-gate 		m_adj(m, inc);
334ab25eeb5Syz # endif /* defined(MENTAT) */
335ab25eeb5Syz #endif /* !defined(_KERNEL) */
3367c478bd9Sstevel@tonic-gate 	COPYBACK(m, off, nlen, newbuf);
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	if (inc != 0) {
3397c478bd9Sstevel@tonic-gate 		ip->ip_len += inc;
3407c478bd9Sstevel@tonic-gate 		fin->fin_dlen += inc;
3417c478bd9Sstevel@tonic-gate 		fin->fin_plen += inc;
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	/*
3457c478bd9Sstevel@tonic-gate 	 * The server may not make the connection back from port 20, but
3467c478bd9Sstevel@tonic-gate 	 * it is the most likely so use it here to check for a conflicting
3477c478bd9Sstevel@tonic-gate 	 * mapping.
3487c478bd9Sstevel@tonic-gate 	 */
3497c478bd9Sstevel@tonic-gate 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
350ab25eeb5Syz 	fi.fin_state = NULL;
351ab25eeb5Syz 	fi.fin_nat = NULL;
3527c478bd9Sstevel@tonic-gate 	fi.fin_flx |= FI_IGNORE;
3537c478bd9Sstevel@tonic-gate 	fi.fin_data[0] = sp;
3547c478bd9Sstevel@tonic-gate 	fi.fin_data[1] = fin->fin_data[1] - 1;
355ab25eeb5Syz 	/*
356ab25eeb5Syz 	 * Add skeleton NAT entry for connection which will come back the
357ab25eeb5Syz 	 * other way.
358ab25eeb5Syz 	 */
3597c478bd9Sstevel@tonic-gate 	if (nat->nat_dir == NAT_OUTBOUND)
3607c478bd9Sstevel@tonic-gate 		nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
3617c478bd9Sstevel@tonic-gate 				     nat->nat_inip, nat->nat_oip);
3627c478bd9Sstevel@tonic-gate 	else
3637c478bd9Sstevel@tonic-gate 		nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
3647c478bd9Sstevel@tonic-gate 				    nat->nat_inip, nat->nat_oip);
3657c478bd9Sstevel@tonic-gate 	if (nat2 == NULL) {
3667c478bd9Sstevel@tonic-gate 		int slen;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 		slen = ip->ip_len;
3697c478bd9Sstevel@tonic-gate 		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
3707c478bd9Sstevel@tonic-gate 		bzero((char *)tcp2, sizeof(*tcp2));
3717c478bd9Sstevel@tonic-gate 		tcp2->th_win = htons(8192);
3727c478bd9Sstevel@tonic-gate 		tcp2->th_sport = htons(sp);
3737c478bd9Sstevel@tonic-gate 		TCP_OFF_A(tcp2, 5);
3747c478bd9Sstevel@tonic-gate 		tcp2->th_flags = TH_SYN;
3757c478bd9Sstevel@tonic-gate 		tcp2->th_dport = 0; /* XXX - don't specify remote port */
3767c478bd9Sstevel@tonic-gate 		fi.fin_data[1] = 0;
3777c478bd9Sstevel@tonic-gate 		fi.fin_dlen = sizeof(*tcp2);
3787c478bd9Sstevel@tonic-gate 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
3797c478bd9Sstevel@tonic-gate 		fi.fin_dp = (char *)tcp2;
380*f4b3ec61Sdh 		fi.fin_fr = &ifsftp->ftppxyfr;
3817c478bd9Sstevel@tonic-gate 		fi.fin_out = nat->nat_dir;
3827c478bd9Sstevel@tonic-gate 		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
3837c478bd9Sstevel@tonic-gate 		swip = ip->ip_src;
3847c478bd9Sstevel@tonic-gate 		swip2 = ip->ip_dst;
3857c478bd9Sstevel@tonic-gate 		if (nat->nat_dir == NAT_OUTBOUND) {
3867c478bd9Sstevel@tonic-gate 			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
3877c478bd9Sstevel@tonic-gate 			ip->ip_src = nat->nat_inip;
3887c478bd9Sstevel@tonic-gate 		} else if (nat->nat_dir == NAT_INBOUND) {
3897c478bd9Sstevel@tonic-gate 			fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
3907c478bd9Sstevel@tonic-gate 			ip->ip_src = nat->nat_oip;
3917c478bd9Sstevel@tonic-gate 		}
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
3947c478bd9Sstevel@tonic-gate 		if (nat->nat_dir == NAT_INBOUND)
3957c478bd9Sstevel@tonic-gate 			flags |= NAT_NOTRULEPORT;
3967c478bd9Sstevel@tonic-gate 		nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir);
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 		if (nat2 != NULL) {
3997c478bd9Sstevel@tonic-gate 			(void) nat_proto(&fi, nat2, IPN_TCP);
4007c478bd9Sstevel@tonic-gate 			nat_update(&fi, nat2, nat->nat_ptr);
4017c478bd9Sstevel@tonic-gate 			fi.fin_ifp = NULL;
4027c478bd9Sstevel@tonic-gate 			if (nat->nat_dir == NAT_INBOUND) {
4037c478bd9Sstevel@tonic-gate 				fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
4047c478bd9Sstevel@tonic-gate 				ip->ip_dst = nat->nat_inip;
4057c478bd9Sstevel@tonic-gate 			}
4067c478bd9Sstevel@tonic-gate 			(void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT);
407ab25eeb5Syz 			if (fi.fin_state != NULL)
408*f4b3ec61Sdh 				fr_statederef(&fi, (ipstate_t **)&fi.fin_state,
409*f4b3ec61Sdh 				    ifs);
4107c478bd9Sstevel@tonic-gate 		}
4117c478bd9Sstevel@tonic-gate 		ip->ip_len = slen;
4127c478bd9Sstevel@tonic-gate 		ip->ip_src = swip;
4137c478bd9Sstevel@tonic-gate 		ip->ip_dst = swip2;
4147c478bd9Sstevel@tonic-gate 	} else {
4157c478bd9Sstevel@tonic-gate 		ipstate_t *is;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		nat_update(&fi, nat2, nat->nat_ptr);
418*f4b3ec61Sdh 		READ_ENTER(&ifs->ifs_ipf_state);
4197c478bd9Sstevel@tonic-gate 		is = nat2->nat_state;
4207c478bd9Sstevel@tonic-gate 		if (is != NULL) {
4217c478bd9Sstevel@tonic-gate 			MUTEX_ENTER(&is->is_lock);
422*f4b3ec61Sdh 			(void)fr_tcp_age(&is->is_sti, &fi, ifs->ifs_ips_tqtqb,
423*f4b3ec61Sdh 				         is->is_flags);
4247c478bd9Sstevel@tonic-gate 			MUTEX_EXIT(&is->is_lock);
4257c478bd9Sstevel@tonic-gate 		}
426*f4b3ec61Sdh 		RWLOCK_EXIT(&ifs->ifs_ipf_state);
4277c478bd9Sstevel@tonic-gate 	}
4287c478bd9Sstevel@tonic-gate 	return APR_INC(inc);
4297c478bd9Sstevel@tonic-gate }
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 
432*f4b3ec61Sdh int ippr_ftp_client(fin, ip, nat, ftp, dlen, ifsftp)
4337c478bd9Sstevel@tonic-gate fr_info_t *fin;
4347c478bd9Sstevel@tonic-gate nat_t *nat;
4357c478bd9Sstevel@tonic-gate ftpinfo_t *ftp;
4367c478bd9Sstevel@tonic-gate ip_t *ip;
4377c478bd9Sstevel@tonic-gate int dlen;
438*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
4397c478bd9Sstevel@tonic-gate {
4407c478bd9Sstevel@tonic-gate 	char *rptr, *wptr, cmd[6], c;
4417c478bd9Sstevel@tonic-gate 	ftpside_t *f;
4427c478bd9Sstevel@tonic-gate 	int inc, i;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	inc = 0;
4457c478bd9Sstevel@tonic-gate 	f = &ftp->ftp_side[0];
4467c478bd9Sstevel@tonic-gate 	rptr = f->ftps_rptr;
4477c478bd9Sstevel@tonic-gate 	wptr = f->ftps_wptr;
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	for (i = 0; (i < 5) && (i < dlen); i++) {
4507c478bd9Sstevel@tonic-gate 		c = rptr[i];
451ab25eeb5Syz 		if (ISALPHA(c)) {
452ab25eeb5Syz 			cmd[i] = TOUPPER(c);
4537c478bd9Sstevel@tonic-gate 		} else {
4547c478bd9Sstevel@tonic-gate 			cmd[i] = c;
4557c478bd9Sstevel@tonic-gate 		}
4567c478bd9Sstevel@tonic-gate 	}
4577c478bd9Sstevel@tonic-gate 	cmd[i] = '\0';
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	ftp->ftp_incok = 0;
4607c478bd9Sstevel@tonic-gate 	if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
4617c478bd9Sstevel@tonic-gate 		if (ftp->ftp_passok == FTPXY_ADOK_1 ||
4627c478bd9Sstevel@tonic-gate 		    ftp->ftp_passok == FTPXY_AUOK_1) {
4637c478bd9Sstevel@tonic-gate 			ftp->ftp_passok = FTPXY_USER_2;
4647c478bd9Sstevel@tonic-gate 			ftp->ftp_incok = 1;
4657c478bd9Sstevel@tonic-gate 		} else {
4667c478bd9Sstevel@tonic-gate 			ftp->ftp_passok = FTPXY_USER_1;
4677c478bd9Sstevel@tonic-gate 			ftp->ftp_incok = 1;
4687c478bd9Sstevel@tonic-gate 		}
4697c478bd9Sstevel@tonic-gate 	} else if (!strncmp(cmd, "AUTH ", 5)) {
4707c478bd9Sstevel@tonic-gate 		ftp->ftp_passok = FTPXY_AUTH_1;
4717c478bd9Sstevel@tonic-gate 		ftp->ftp_incok = 1;
4727c478bd9Sstevel@tonic-gate 	} else if (!strncmp(cmd, "PASS ", 5)) {
4737c478bd9Sstevel@tonic-gate 		if (ftp->ftp_passok == FTPXY_USOK_1) {
4747c478bd9Sstevel@tonic-gate 			ftp->ftp_passok = FTPXY_PASS_1;
4757c478bd9Sstevel@tonic-gate 			ftp->ftp_incok = 1;
4767c478bd9Sstevel@tonic-gate 		} else if (ftp->ftp_passok == FTPXY_USOK_2) {
4777c478bd9Sstevel@tonic-gate 			ftp->ftp_passok = FTPXY_PASS_2;
4787c478bd9Sstevel@tonic-gate 			ftp->ftp_incok = 1;
4797c478bd9Sstevel@tonic-gate 		}
4807c478bd9Sstevel@tonic-gate 	} else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
4817c478bd9Sstevel@tonic-gate 		   !strncmp(cmd, "ADAT ", 5)) {
4827c478bd9Sstevel@tonic-gate 		ftp->ftp_passok = FTPXY_ADAT_1;
4837c478bd9Sstevel@tonic-gate 		ftp->ftp_incok = 1;
4847c478bd9Sstevel@tonic-gate 	} else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
4857c478bd9Sstevel@tonic-gate 		    ftp->ftp_passok == FTPXY_PAOK_2) &&
4867c478bd9Sstevel@tonic-gate 		 !strncmp(cmd, "ACCT ", 5)) {
4877c478bd9Sstevel@tonic-gate 		ftp->ftp_passok = FTPXY_ACCT_1;
4887c478bd9Sstevel@tonic-gate 		ftp->ftp_incok = 1;
489*f4b3ec61Sdh 	} else if ((ftp->ftp_passok == FTPXY_GO) &&
490*f4b3ec61Sdh 		 !ifsftp->ippr_ftp_pasvonly &&
4917c478bd9Sstevel@tonic-gate 		 !strncmp(cmd, "PORT ", 5)) {
492*f4b3ec61Sdh 		inc = ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp);
493*f4b3ec61Sdh 	} else if (ifsftp->ippr_ftp_insecure && !ifsftp->ippr_ftp_pasvonly &&
4947c478bd9Sstevel@tonic-gate 		   !strncmp(cmd, "PORT ", 5)) {
495*f4b3ec61Sdh 		inc = ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp);
4967c478bd9Sstevel@tonic-gate 	}
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	while ((*rptr++ != '\n') && (rptr < wptr))
4997c478bd9Sstevel@tonic-gate 		;
5007c478bd9Sstevel@tonic-gate 	f->ftps_rptr = rptr;
5017c478bd9Sstevel@tonic-gate 	return inc;
5027c478bd9Sstevel@tonic-gate }
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 
505*f4b3ec61Sdh int ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp)
5067c478bd9Sstevel@tonic-gate fr_info_t *fin;
5077c478bd9Sstevel@tonic-gate ip_t *ip;
5087c478bd9Sstevel@tonic-gate nat_t *nat;
5097c478bd9Sstevel@tonic-gate ftpinfo_t *ftp;
5107c478bd9Sstevel@tonic-gate int dlen;
511*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate 	u_int a1, a2, a3, a4, data_ip;
5147c478bd9Sstevel@tonic-gate 	char newbuf[IPF_FTPBUFSZ];
515ab25eeb5Syz 	char *s, *brackets[2];
5167c478bd9Sstevel@tonic-gate 	u_short a5, a6;
5177c478bd9Sstevel@tonic-gate 	ftpside_t *f;
5187c478bd9Sstevel@tonic-gate 
519*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_forcepasv != 0 &&
5207c478bd9Sstevel@tonic-gate 	    ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) {
521*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 0)
522ab25eeb5Syz 			printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
523ab25eeb5Syz 			       ftp->ftp_side[0].ftps_cmds);
5247c478bd9Sstevel@tonic-gate 		return 0;
5257c478bd9Sstevel@tonic-gate 	}
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	f = &ftp->ftp_side[1];
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate #define	PASV_REPLEN	24
5307c478bd9Sstevel@tonic-gate 	/*
5317c478bd9Sstevel@tonic-gate 	 * Check for PASV reply message.
5327c478bd9Sstevel@tonic-gate 	 */
5337c478bd9Sstevel@tonic-gate 	if (dlen < IPF_MIN227LEN) {
534*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
535ab25eeb5Syz 			printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
536ab25eeb5Syz 			       dlen);
5377c478bd9Sstevel@tonic-gate 		return 0;
5387c478bd9Sstevel@tonic-gate 	} else if (strncmp(f->ftps_rptr,
5397c478bd9Sstevel@tonic-gate 			   "227 Entering Passive Mod", PASV_REPLEN)) {
540*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 0)
541ab25eeb5Syz 			printf("ippr_ftp_pasv:%d reply wrong\n", 227);
5427c478bd9Sstevel@tonic-gate 		return 0;
5437c478bd9Sstevel@tonic-gate 	}
5447c478bd9Sstevel@tonic-gate 
545ab25eeb5Syz 	brackets[0] = "";
546ab25eeb5Syz 	brackets[1] = "";
5477c478bd9Sstevel@tonic-gate 	/*
5487c478bd9Sstevel@tonic-gate 	 * Skip the PASV reply + space
5497c478bd9Sstevel@tonic-gate 	 */
5507c478bd9Sstevel@tonic-gate 	s = f->ftps_rptr + PASV_REPLEN;
551ab25eeb5Syz 	while (*s && !ISDIGIT(*s)) {
552ab25eeb5Syz 		if (*s == '(') {
553ab25eeb5Syz 			brackets[0] = "(";
554ab25eeb5Syz 			brackets[1] = ")";
555ab25eeb5Syz 		}
5567c478bd9Sstevel@tonic-gate 		s++;
557ab25eeb5Syz 	}
558ab25eeb5Syz 
5597c478bd9Sstevel@tonic-gate 	/*
5607c478bd9Sstevel@tonic-gate 	 * Pick out the address components, two at a time.
5617c478bd9Sstevel@tonic-gate 	 */
5627c478bd9Sstevel@tonic-gate 	a1 = ippr_ftp_atoi(&s);
5637c478bd9Sstevel@tonic-gate 	if (s == NULL) {
564*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
565ab25eeb5Syz 			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1);
5667c478bd9Sstevel@tonic-gate 		return 0;
5677c478bd9Sstevel@tonic-gate 	}
5687c478bd9Sstevel@tonic-gate 	a2 = ippr_ftp_atoi(&s);
5697c478bd9Sstevel@tonic-gate 	if (s == NULL) {
570*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
571ab25eeb5Syz 			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2);
5727c478bd9Sstevel@tonic-gate 		return 0;
5737c478bd9Sstevel@tonic-gate 	}
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	/*
5767c478bd9Sstevel@tonic-gate 	 * check that IP address in the PASV reply is the same as the
5777c478bd9Sstevel@tonic-gate 	 * sender of the command - prevents using PASV for port scanning.
5787c478bd9Sstevel@tonic-gate 	 */
5797c478bd9Sstevel@tonic-gate 	a1 <<= 16;
5807c478bd9Sstevel@tonic-gate 	a1 |= a2;
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	if (((nat->nat_dir == NAT_INBOUND) &&
5837c478bd9Sstevel@tonic-gate 	     (a1 != ntohl(nat->nat_inip.s_addr))) ||
5847c478bd9Sstevel@tonic-gate 	    ((nat->nat_dir == NAT_OUTBOUND) &&
5857c478bd9Sstevel@tonic-gate 	     (a1 != ntohl(nat->nat_oip.s_addr)))) {
586*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 0)
587ab25eeb5Syz 			printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1");
5887c478bd9Sstevel@tonic-gate 		return 0;
5897c478bd9Sstevel@tonic-gate 	}
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	a5 = ippr_ftp_atoi(&s);
5927c478bd9Sstevel@tonic-gate 	if (s == NULL) {
593*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
594ab25eeb5Syz 			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3);
5957c478bd9Sstevel@tonic-gate 		return 0;
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	if (*s == ')')
5997c478bd9Sstevel@tonic-gate 		s++;
6007c478bd9Sstevel@tonic-gate 	if (*s == '.')
6017c478bd9Sstevel@tonic-gate 		s++;
6027c478bd9Sstevel@tonic-gate 	if (*s == '\n')
6037c478bd9Sstevel@tonic-gate 		s--;
6047c478bd9Sstevel@tonic-gate 	/*
6057c478bd9Sstevel@tonic-gate 	 * check for CR-LF at the end.
6067c478bd9Sstevel@tonic-gate 	 */
6077c478bd9Sstevel@tonic-gate 	if ((*s == '\r') && (*(s + 1) == '\n')) {
6087c478bd9Sstevel@tonic-gate 		s += 2;
6097c478bd9Sstevel@tonic-gate 	} else {
610*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1)
611ab25eeb5Syz 			printf("ippr_ftp_pasv:missing %s", "cr-lf\n");
6127c478bd9Sstevel@tonic-gate 		return 0;
6137c478bd9Sstevel@tonic-gate 	}
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	a6 = a5 & 0xff;
6167c478bd9Sstevel@tonic-gate 	a5 >>= 8;
6177c478bd9Sstevel@tonic-gate 	/*
6187c478bd9Sstevel@tonic-gate 	 * Calculate new address parts for 227 reply
6197c478bd9Sstevel@tonic-gate 	 */
6207c478bd9Sstevel@tonic-gate 	if (nat->nat_dir == NAT_INBOUND) {
6217c478bd9Sstevel@tonic-gate 		data_ip = nat->nat_outip.s_addr;
6227c478bd9Sstevel@tonic-gate 		a1 = ntohl(data_ip);
6237c478bd9Sstevel@tonic-gate 	} else
6247c478bd9Sstevel@tonic-gate 		data_ip = htonl(a1);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	a2 = (a1 >> 16) & 0xff;
6277c478bd9Sstevel@tonic-gate 	a3 = (a1 >> 8) & 0xff;
6287c478bd9Sstevel@tonic-gate 	a4 = a1 & 0xff;
6297c478bd9Sstevel@tonic-gate 	a1 >>= 24;
6307c478bd9Sstevel@tonic-gate 
631ab25eeb5Syz #if defined(SNPRINTF) && defined(_KERNEL)
632ab25eeb5Syz 	(void) SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
633ab25eeb5Syz 		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
634ab25eeb5Syz 		a5, a6, brackets[1]);
6357c478bd9Sstevel@tonic-gate #else
636ab25eeb5Syz 	(void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
637ab25eeb5Syz 		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
638ab25eeb5Syz 		a5, a6, brackets[1]);
6397c478bd9Sstevel@tonic-gate #endif
6407c478bd9Sstevel@tonic-gate 	return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6),
641*f4b3ec61Sdh 				  newbuf, s, data_ip, ifsftp);
6427c478bd9Sstevel@tonic-gate }
6437c478bd9Sstevel@tonic-gate 
644*f4b3ec61Sdh int ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip, ifsftp)
6457c478bd9Sstevel@tonic-gate fr_info_t *fin;
6467c478bd9Sstevel@tonic-gate ip_t *ip;
6477c478bd9Sstevel@tonic-gate nat_t *nat;
6487c478bd9Sstevel@tonic-gate ftpside_t *f;
6497c478bd9Sstevel@tonic-gate u_int port;
6507c478bd9Sstevel@tonic-gate char *newmsg;
6517c478bd9Sstevel@tonic-gate char *s;
6527c478bd9Sstevel@tonic-gate u_int data_ip;
653*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
6547c478bd9Sstevel@tonic-gate {
6557c478bd9Sstevel@tonic-gate 	int inc, off, nflags, sflags;
6567c478bd9Sstevel@tonic-gate 	tcphdr_t *tcp, tcph, *tcp2;
6577c478bd9Sstevel@tonic-gate 	struct in_addr swip, swip2;
6587c478bd9Sstevel@tonic-gate 	struct in_addr data_addr;
6597c478bd9Sstevel@tonic-gate 	size_t nlen, olen;
6607c478bd9Sstevel@tonic-gate 	fr_info_t fi;
6617c478bd9Sstevel@tonic-gate 	nat_t *nat2;
6627c478bd9Sstevel@tonic-gate 	mb_t *m;
663*f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate 	m = fin->fin_m;
6667c478bd9Sstevel@tonic-gate 	tcp = (tcphdr_t *)fin->fin_dp;
667ab25eeb5Syz 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	data_addr.s_addr = data_ip;
6707c478bd9Sstevel@tonic-gate 	tcp2 = &tcph;
6717c478bd9Sstevel@tonic-gate 	inc = 0;
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	olen = s - f->ftps_rptr;
6757c478bd9Sstevel@tonic-gate 	nlen = strlen(newmsg);
6767c478bd9Sstevel@tonic-gate 	inc = nlen - olen;
6777c478bd9Sstevel@tonic-gate 	if ((inc + ip->ip_len) > 65535) {
678*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 0)
679ab25eeb5Syz 			printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
680ab25eeb5Syz 			       inc);
6817c478bd9Sstevel@tonic-gate 		return 0;
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate #if !defined(_KERNEL)
685ab25eeb5Syz 	bcopy(newmsg, MTOD(m, char *) + off, nlen);
6867c478bd9Sstevel@tonic-gate #else
6877c478bd9Sstevel@tonic-gate # if defined(MENTAT)
6887c478bd9Sstevel@tonic-gate 	if (inc < 0)
6897c478bd9Sstevel@tonic-gate 		(void)adjmsg(m, inc);
6907c478bd9Sstevel@tonic-gate # else /* defined(MENTAT) */
691ab25eeb5Syz 	/*
692ab25eeb5Syz 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
693ab25eeb5Syz 	 * mean remove -len bytes from the end of the packet.
694ab25eeb5Syz 	 * The mbuf chain will be extended if necessary by m_copyback().
695ab25eeb5Syz 	 */
6967c478bd9Sstevel@tonic-gate 	if (inc < 0)
6977c478bd9Sstevel@tonic-gate 		m_adj(m, inc);
6987c478bd9Sstevel@tonic-gate # endif /* defined(MENTAT) */
6997c478bd9Sstevel@tonic-gate #endif /* !defined(_KERNEL) */
7007c478bd9Sstevel@tonic-gate 	COPYBACK(m, off, nlen, newmsg);
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate 	if (inc != 0) {
7037c478bd9Sstevel@tonic-gate 		ip->ip_len += inc;
7047c478bd9Sstevel@tonic-gate 		fin->fin_dlen += inc;
7057c478bd9Sstevel@tonic-gate 		fin->fin_plen += inc;
7067c478bd9Sstevel@tonic-gate 	}
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	/*
7097c478bd9Sstevel@tonic-gate 	 * Add skeleton NAT entry for connection which will come back the
7107c478bd9Sstevel@tonic-gate 	 * other way.
7117c478bd9Sstevel@tonic-gate 	 */
7127c478bd9Sstevel@tonic-gate 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
713ab25eeb5Syz 	fi.fin_state = NULL;
714ab25eeb5Syz 	fi.fin_nat = NULL;
7157c478bd9Sstevel@tonic-gate 	fi.fin_flx |= FI_IGNORE;
7167c478bd9Sstevel@tonic-gate 	fi.fin_data[0] = 0;
7177c478bd9Sstevel@tonic-gate 	fi.fin_data[1] = port;
7187c478bd9Sstevel@tonic-gate 	nflags = IPN_TCP|SI_W_SPORT;
719*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_pasvrdr && f->ftps_ifp)
7207c478bd9Sstevel@tonic-gate 		nflags |= SI_W_DPORT;
7217c478bd9Sstevel@tonic-gate 	if (nat->nat_dir == NAT_OUTBOUND)
7227c478bd9Sstevel@tonic-gate 		nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH,
7237c478bd9Sstevel@tonic-gate 				     nat->nat_p, nat->nat_inip, nat->nat_oip);
7247c478bd9Sstevel@tonic-gate 	else
7257c478bd9Sstevel@tonic-gate 		nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH,
7267c478bd9Sstevel@tonic-gate 				    nat->nat_p, nat->nat_inip, nat->nat_oip);
7277c478bd9Sstevel@tonic-gate 	if (nat2 == NULL) {
7287c478bd9Sstevel@tonic-gate 		int slen;
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 		slen = ip->ip_len;
7317c478bd9Sstevel@tonic-gate 		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
7327c478bd9Sstevel@tonic-gate 		bzero((char *)tcp2, sizeof(*tcp2));
7337c478bd9Sstevel@tonic-gate 		tcp2->th_win = htons(8192);
7347c478bd9Sstevel@tonic-gate 		tcp2->th_sport = 0;		/* XXX - fake it for nat_new */
7357c478bd9Sstevel@tonic-gate 		TCP_OFF_A(tcp2, 5);
7367c478bd9Sstevel@tonic-gate 		tcp2->th_flags = TH_SYN;
7377c478bd9Sstevel@tonic-gate 		fi.fin_data[1] = port;
7387c478bd9Sstevel@tonic-gate 		fi.fin_dlen = sizeof(*tcp2);
7397c478bd9Sstevel@tonic-gate 		tcp2->th_dport = htons(port);
7407c478bd9Sstevel@tonic-gate 		fi.fin_data[0] = 0;
7417c478bd9Sstevel@tonic-gate 		fi.fin_dp = (char *)tcp2;
7427c478bd9Sstevel@tonic-gate 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
743*f4b3ec61Sdh 		fi.fin_fr = &ifsftp->ftppxyfr;
7447c478bd9Sstevel@tonic-gate 		fi.fin_out = nat->nat_dir;
7457c478bd9Sstevel@tonic-gate 		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
7467c478bd9Sstevel@tonic-gate 		swip = ip->ip_src;
7477c478bd9Sstevel@tonic-gate 		swip2 = ip->ip_dst;
7487c478bd9Sstevel@tonic-gate 		if (nat->nat_dir == NAT_OUTBOUND) {
7497c478bd9Sstevel@tonic-gate 			fi.fin_fi.fi_daddr = data_addr.s_addr;
7507c478bd9Sstevel@tonic-gate 			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
7517c478bd9Sstevel@tonic-gate 			ip->ip_dst = data_addr;
7527c478bd9Sstevel@tonic-gate 			ip->ip_src = nat->nat_inip;
7537c478bd9Sstevel@tonic-gate 		} else if (nat->nat_dir == NAT_INBOUND) {
7547c478bd9Sstevel@tonic-gate 			fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
7557c478bd9Sstevel@tonic-gate 			fi.fin_fi.fi_daddr = nat->nat_outip.s_addr;
7567c478bd9Sstevel@tonic-gate 			ip->ip_src = nat->nat_oip;
7577c478bd9Sstevel@tonic-gate 			ip->ip_dst = nat->nat_outip;
7587c478bd9Sstevel@tonic-gate 		}
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 		sflags = nflags;
7617c478bd9Sstevel@tonic-gate 		nflags |= NAT_SLAVE;
7627c478bd9Sstevel@tonic-gate 		if (nat->nat_dir == NAT_INBOUND)
7637c478bd9Sstevel@tonic-gate 			nflags |= NAT_NOTRULEPORT;
7647c478bd9Sstevel@tonic-gate 		nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir);
7657c478bd9Sstevel@tonic-gate 		if (nat2 != NULL) {
7667c478bd9Sstevel@tonic-gate 			(void) nat_proto(&fi, nat2, IPN_TCP);
7677c478bd9Sstevel@tonic-gate 			nat_update(&fi, nat2, nat->nat_ptr);
7687c478bd9Sstevel@tonic-gate 			fi.fin_ifp = NULL;
7697c478bd9Sstevel@tonic-gate 			if (nat->nat_dir == NAT_INBOUND) {
7707c478bd9Sstevel@tonic-gate 				fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
7717c478bd9Sstevel@tonic-gate 				ip->ip_dst = nat->nat_inip;
7727c478bd9Sstevel@tonic-gate 			}
7737c478bd9Sstevel@tonic-gate 			(void) fr_addstate(&fi, &nat2->nat_state, sflags);
774ab25eeb5Syz 			if (fi.fin_state != NULL)
775*f4b3ec61Sdh 				fr_statederef(&fi, (ipstate_t **)&fi.fin_state,
776*f4b3ec61Sdh 				    ifs);
7777c478bd9Sstevel@tonic-gate 		}
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 		ip->ip_len = slen;
7807c478bd9Sstevel@tonic-gate 		ip->ip_src = swip;
7817c478bd9Sstevel@tonic-gate 		ip->ip_dst = swip2;
7827c478bd9Sstevel@tonic-gate 	} else {
7837c478bd9Sstevel@tonic-gate 		ipstate_t *is;
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 		nat_update(&fi, nat2, nat->nat_ptr);
786*f4b3ec61Sdh 		READ_ENTER(&ifs->ifs_ipf_state);
7877c478bd9Sstevel@tonic-gate 		is = nat2->nat_state;
7887c478bd9Sstevel@tonic-gate 		if (is != NULL) {
7897c478bd9Sstevel@tonic-gate 			MUTEX_ENTER(&is->is_lock);
790*f4b3ec61Sdh 			(void) fr_tcp_age(&is->is_sti, &fi, ifs->ifs_ips_tqtqb,
791*f4b3ec61Sdh 					  is->is_flags);
7927c478bd9Sstevel@tonic-gate 			MUTEX_EXIT(&is->is_lock);
7937c478bd9Sstevel@tonic-gate 		}
794*f4b3ec61Sdh 		RWLOCK_EXIT(&ifs->ifs_ipf_state);
7957c478bd9Sstevel@tonic-gate 	}
7967c478bd9Sstevel@tonic-gate 	return inc;
7977c478bd9Sstevel@tonic-gate }
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 
800*f4b3ec61Sdh int ippr_ftp_server(fin, ip, nat, ftp, dlen, ifsftp)
8017c478bd9Sstevel@tonic-gate fr_info_t *fin;
8027c478bd9Sstevel@tonic-gate ip_t *ip;
8037c478bd9Sstevel@tonic-gate nat_t *nat;
8047c478bd9Sstevel@tonic-gate ftpinfo_t *ftp;
8057c478bd9Sstevel@tonic-gate int dlen;
806*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
8077c478bd9Sstevel@tonic-gate {
8087c478bd9Sstevel@tonic-gate 	char *rptr, *wptr;
8097c478bd9Sstevel@tonic-gate 	ftpside_t *f;
8107c478bd9Sstevel@tonic-gate 	int inc;
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	inc = 0;
8137c478bd9Sstevel@tonic-gate 	f = &ftp->ftp_side[1];
8147c478bd9Sstevel@tonic-gate 	rptr = f->ftps_rptr;
8157c478bd9Sstevel@tonic-gate 	wptr = f->ftps_wptr;
8167c478bd9Sstevel@tonic-gate 
817ab25eeb5Syz 	if (*rptr == ' ')
818ab25eeb5Syz 		goto server_cmd_ok;
819ab25eeb5Syz 	if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
8207c478bd9Sstevel@tonic-gate 		return 0;
8217c478bd9Sstevel@tonic-gate 	if (ftp->ftp_passok == FTPXY_GO) {
8227c478bd9Sstevel@tonic-gate 		if (!strncmp(rptr, "227 ", 4))
823*f4b3ec61Sdh 			inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp);
8247c478bd9Sstevel@tonic-gate 		else if (!strncmp(rptr, "229 ", 4))
825*f4b3ec61Sdh 			inc = ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp);
826*f4b3ec61Sdh 	} else if (ifsftp->ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
827*f4b3ec61Sdh 		inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp);
828*f4b3ec61Sdh 	} else if (ifsftp->ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
829*f4b3ec61Sdh 		inc = ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp);
8307c478bd9Sstevel@tonic-gate 	} else if (*rptr == '5' || *rptr == '4')
8317c478bd9Sstevel@tonic-gate 		ftp->ftp_passok = FTPXY_INIT;
8327c478bd9Sstevel@tonic-gate 	else if (ftp->ftp_incok) {
8337c478bd9Sstevel@tonic-gate 		if (*rptr == '3') {
8347c478bd9Sstevel@tonic-gate 			if (ftp->ftp_passok == FTPXY_ACCT_1)
8357c478bd9Sstevel@tonic-gate 				ftp->ftp_passok = FTPXY_GO;
8367c478bd9Sstevel@tonic-gate 			else
8377c478bd9Sstevel@tonic-gate 				ftp->ftp_passok++;
8387c478bd9Sstevel@tonic-gate 		} else if (*rptr == '2') {
8397c478bd9Sstevel@tonic-gate 			switch (ftp->ftp_passok)
8407c478bd9Sstevel@tonic-gate 			{
8417c478bd9Sstevel@tonic-gate 			case FTPXY_USER_1 :
8427c478bd9Sstevel@tonic-gate 			case FTPXY_USER_2 :
8437c478bd9Sstevel@tonic-gate 			case FTPXY_PASS_1 :
8447c478bd9Sstevel@tonic-gate 			case FTPXY_PASS_2 :
8457c478bd9Sstevel@tonic-gate 			case FTPXY_ACCT_1 :
8467c478bd9Sstevel@tonic-gate 				ftp->ftp_passok = FTPXY_GO;
8477c478bd9Sstevel@tonic-gate 				break;
8487c478bd9Sstevel@tonic-gate 			default :
8497c478bd9Sstevel@tonic-gate 				ftp->ftp_passok += 3;
8507c478bd9Sstevel@tonic-gate 				break;
8517c478bd9Sstevel@tonic-gate 			}
8527c478bd9Sstevel@tonic-gate 		}
8537c478bd9Sstevel@tonic-gate 	}
854ab25eeb5Syz server_cmd_ok:
8557c478bd9Sstevel@tonic-gate 	ftp->ftp_incok = 0;
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	while ((*rptr++ != '\n') && (rptr < wptr))
8587c478bd9Sstevel@tonic-gate 		;
8597c478bd9Sstevel@tonic-gate 	f->ftps_rptr = rptr;
8607c478bd9Sstevel@tonic-gate 	return inc;
8617c478bd9Sstevel@tonic-gate }
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate /*
8657c478bd9Sstevel@tonic-gate  * Look to see if the buffer starts with something which we recognise as
8667c478bd9Sstevel@tonic-gate  * being the correct syntax for the FTP protocol.
8677c478bd9Sstevel@tonic-gate  */
868*f4b3ec61Sdh int ippr_ftp_client_valid(ftps, buf, len, ifsftp)
8697c478bd9Sstevel@tonic-gate ftpside_t *ftps;
8707c478bd9Sstevel@tonic-gate char *buf;
8717c478bd9Sstevel@tonic-gate size_t len;
872*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
8737c478bd9Sstevel@tonic-gate {
874ab25eeb5Syz 	register char *s, c, pc;
8757c478bd9Sstevel@tonic-gate 	register size_t i = len;
8767c478bd9Sstevel@tonic-gate 	char cmd[5];
8777c478bd9Sstevel@tonic-gate 
878ab25eeb5Syz 	s = buf;
879ab25eeb5Syz 
880ab25eeb5Syz 	if (ftps->ftps_junk == 1)
881ab25eeb5Syz 		return 1;
882ab25eeb5Syz 
8837c478bd9Sstevel@tonic-gate 	if (i < 5) {
884*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 3)
885ab25eeb5Syz 			printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i);
8867c478bd9Sstevel@tonic-gate 		return 2;
8877c478bd9Sstevel@tonic-gate 	}
888ab25eeb5Syz 
8897c478bd9Sstevel@tonic-gate 	i--;
890ab25eeb5Syz 	c = *s++;
8917c478bd9Sstevel@tonic-gate 
892ab25eeb5Syz 	if (ISALPHA(c)) {
893ab25eeb5Syz 		cmd[0] = TOUPPER(c);
8947c478bd9Sstevel@tonic-gate 		c = *s++;
8957c478bd9Sstevel@tonic-gate 		i--;
896ab25eeb5Syz 		if (ISALPHA(c)) {
897ab25eeb5Syz 			cmd[1] = TOUPPER(c);
8987c478bd9Sstevel@tonic-gate 			c = *s++;
8997c478bd9Sstevel@tonic-gate 			i--;
900ab25eeb5Syz 			if (ISALPHA(c)) {
901ab25eeb5Syz 				cmd[2] = TOUPPER(c);
9027c478bd9Sstevel@tonic-gate 				c = *s++;
9037c478bd9Sstevel@tonic-gate 				i--;
904ab25eeb5Syz 				if (ISALPHA(c)) {
905ab25eeb5Syz 					cmd[3] = TOUPPER(c);
9067c478bd9Sstevel@tonic-gate 					c = *s++;
9077c478bd9Sstevel@tonic-gate 					i--;
9087c478bd9Sstevel@tonic-gate 					if ((c != ' ') && (c != '\r'))
9097c478bd9Sstevel@tonic-gate 						goto bad_client_command;
9107c478bd9Sstevel@tonic-gate 				} else if ((c != ' ') && (c != '\r'))
9117c478bd9Sstevel@tonic-gate 					goto bad_client_command;
9127c478bd9Sstevel@tonic-gate 			} else
9137c478bd9Sstevel@tonic-gate 				goto bad_client_command;
9147c478bd9Sstevel@tonic-gate 		} else
9157c478bd9Sstevel@tonic-gate 			goto bad_client_command;
9167c478bd9Sstevel@tonic-gate 	} else {
9177c478bd9Sstevel@tonic-gate bad_client_command:
918*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 3)
919ab25eeb5Syz 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*s]\n",
920ab25eeb5Syz 			       "ippr_ftp_client_valid",
921ab25eeb5Syz 			       ftps->ftps_junk, (int)len, (int)i, c,
922ab25eeb5Syz 			       (int)len, buf);
9237c478bd9Sstevel@tonic-gate 		return 1;
9247c478bd9Sstevel@tonic-gate 	}
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 	for (; i; i--) {
927ab25eeb5Syz 		pc = c;
9287c478bd9Sstevel@tonic-gate 		c = *s++;
929ab25eeb5Syz 		if ((pc == '\r') && (c == '\n')) {
9307c478bd9Sstevel@tonic-gate 			cmd[4] = '\0';
9317c478bd9Sstevel@tonic-gate 			if (!strcmp(cmd, "PASV"))
9327c478bd9Sstevel@tonic-gate 				ftps->ftps_cmds = FTPXY_C_PASV;
9337c478bd9Sstevel@tonic-gate 			else
9347c478bd9Sstevel@tonic-gate 				ftps->ftps_cmds = 0;
9357c478bd9Sstevel@tonic-gate 			return 0;
9367c478bd9Sstevel@tonic-gate 		}
9377c478bd9Sstevel@tonic-gate 	}
9387c478bd9Sstevel@tonic-gate #if !defined(_KERNEL)
939ab25eeb5Syz 	printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n",
940ab25eeb5Syz 	       (int)len, (int)len, buf);
9417c478bd9Sstevel@tonic-gate #endif
9427c478bd9Sstevel@tonic-gate 	return 2;
9437c478bd9Sstevel@tonic-gate }
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 
946*f4b3ec61Sdh int ippr_ftp_server_valid(ftps, buf, len, ifsftp)
9477c478bd9Sstevel@tonic-gate ftpside_t *ftps;
9487c478bd9Sstevel@tonic-gate char *buf;
9497c478bd9Sstevel@tonic-gate size_t len;
950*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
9517c478bd9Sstevel@tonic-gate {
952ab25eeb5Syz 	register char *s, c, pc;
9537c478bd9Sstevel@tonic-gate 	register size_t i = len;
9547c478bd9Sstevel@tonic-gate 	int cmd;
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 	s = buf;
9577c478bd9Sstevel@tonic-gate 	cmd = 0;
958ab25eeb5Syz 
959ab25eeb5Syz 	if (ftps->ftps_junk == 1)
960ab25eeb5Syz 		return 1;
961ab25eeb5Syz 
962ab25eeb5Syz 	if (i < 5) {
963*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 3)
964ab25eeb5Syz 			printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i);
965ab25eeb5Syz 		return 2;
966ab25eeb5Syz 	}
967ab25eeb5Syz 
968ab25eeb5Syz 	c = *s++;
9697c478bd9Sstevel@tonic-gate 	i--;
970ab25eeb5Syz 	if (c == ' ')
971ab25eeb5Syz 		goto search_eol;
9727c478bd9Sstevel@tonic-gate 
973ab25eeb5Syz 	if (ISDIGIT(c)) {
9747c478bd9Sstevel@tonic-gate 		cmd = (c - '0') * 100;
9757c478bd9Sstevel@tonic-gate 		c = *s++;
9767c478bd9Sstevel@tonic-gate 		i--;
977ab25eeb5Syz 		if (ISDIGIT(c)) {
9787c478bd9Sstevel@tonic-gate 			cmd += (c - '0') * 10;
9797c478bd9Sstevel@tonic-gate 			c = *s++;
9807c478bd9Sstevel@tonic-gate 			i--;
981ab25eeb5Syz 			if (ISDIGIT(c)) {
9827c478bd9Sstevel@tonic-gate 				cmd += (c - '0');
9837c478bd9Sstevel@tonic-gate 				c = *s++;
9847c478bd9Sstevel@tonic-gate 				i--;
9857c478bd9Sstevel@tonic-gate 				if ((c != '-') && (c != ' '))
9867c478bd9Sstevel@tonic-gate 					goto bad_server_command;
9877c478bd9Sstevel@tonic-gate 			} else
9887c478bd9Sstevel@tonic-gate 				goto bad_server_command;
9897c478bd9Sstevel@tonic-gate 		} else
9907c478bd9Sstevel@tonic-gate 			goto bad_server_command;
9917c478bd9Sstevel@tonic-gate 	} else {
9927c478bd9Sstevel@tonic-gate bad_server_command:
993*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 3)
994ab25eeb5Syz 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*s]\n",
995ab25eeb5Syz 			       "ippr_ftp_server_valid",
996ab25eeb5Syz 			       ftps->ftps_junk, (int)len, (int)i,
997ab25eeb5Syz 			       c, (int)len, buf);
9987c478bd9Sstevel@tonic-gate 		return 1;
9997c478bd9Sstevel@tonic-gate 	}
1000ab25eeb5Syz search_eol:
10017c478bd9Sstevel@tonic-gate 	for (; i; i--) {
1002ab25eeb5Syz 		pc = c;
10037c478bd9Sstevel@tonic-gate 		c = *s++;
1004ab25eeb5Syz 		if ((pc == '\r') && (c == '\n')) {
10057c478bd9Sstevel@tonic-gate 			ftps->ftps_cmds = cmd;
10067c478bd9Sstevel@tonic-gate 			return 0;
10077c478bd9Sstevel@tonic-gate 		}
10087c478bd9Sstevel@tonic-gate 	}
1009*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_debug > 3)
1010ab25eeb5Syz 		printf("ippr_ftp_server_valid:junk after cmd[%*s]\n",
1011ab25eeb5Syz 		       (int)len, buf);
10127c478bd9Sstevel@tonic-gate 	return 2;
10137c478bd9Sstevel@tonic-gate }
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 
1016*f4b3ec61Sdh int ippr_ftp_valid(ftp, side, buf, len, ifsftp)
10177c478bd9Sstevel@tonic-gate ftpinfo_t *ftp;
10187c478bd9Sstevel@tonic-gate int side;
10197c478bd9Sstevel@tonic-gate char *buf;
10207c478bd9Sstevel@tonic-gate size_t len;
1021*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
10227c478bd9Sstevel@tonic-gate {
10237c478bd9Sstevel@tonic-gate 	ftpside_t *ftps;
10247c478bd9Sstevel@tonic-gate 	int ret;
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	ftps = &ftp->ftp_side[side];
10277c478bd9Sstevel@tonic-gate 
10287c478bd9Sstevel@tonic-gate 	if (side == 0)
1029*f4b3ec61Sdh 		ret = ippr_ftp_client_valid(ftps, buf, len, ifsftp);
10307c478bd9Sstevel@tonic-gate 	else
1031*f4b3ec61Sdh 		ret = ippr_ftp_server_valid(ftps, buf, len, ifsftp);
10327c478bd9Sstevel@tonic-gate 	return ret;
10337c478bd9Sstevel@tonic-gate }
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate /*
10377c478bd9Sstevel@tonic-gate  * For map rules, the following applies:
10387c478bd9Sstevel@tonic-gate  * rv == 0 for outbound processing,
10397c478bd9Sstevel@tonic-gate  * rv == 1 for inbound processing.
10407c478bd9Sstevel@tonic-gate  * For rdr rules, the following applies:
10417c478bd9Sstevel@tonic-gate  * rv == 0 for inbound processing,
10427c478bd9Sstevel@tonic-gate  * rv == 1 for outbound processing.
10437c478bd9Sstevel@tonic-gate  */
1044*f4b3ec61Sdh int ippr_ftp_process(fin, nat, ftp, rv, ifsftp)
10457c478bd9Sstevel@tonic-gate fr_info_t *fin;
10467c478bd9Sstevel@tonic-gate nat_t *nat;
10477c478bd9Sstevel@tonic-gate ftpinfo_t *ftp;
10487c478bd9Sstevel@tonic-gate int rv;
1049*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
10507c478bd9Sstevel@tonic-gate {
10517c478bd9Sstevel@tonic-gate 	int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff;
1052ab25eeb5Syz 	char *rptr, *wptr, *s;
10537c478bd9Sstevel@tonic-gate 	u_32_t thseq, thack;
10547c478bd9Sstevel@tonic-gate 	ap_session_t *aps;
10557c478bd9Sstevel@tonic-gate 	ftpside_t *f, *t;
10567c478bd9Sstevel@tonic-gate 	tcphdr_t *tcp;
10577c478bd9Sstevel@tonic-gate 	ip_t *ip;
10587c478bd9Sstevel@tonic-gate 	mb_t *m;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	m = fin->fin_m;
10617c478bd9Sstevel@tonic-gate 	ip = fin->fin_ip;
10627c478bd9Sstevel@tonic-gate 	tcp = (tcphdr_t *)fin->fin_dp;
1063ab25eeb5Syz 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate 	f = &ftp->ftp_side[rv];
10667c478bd9Sstevel@tonic-gate 	t = &ftp->ftp_side[1 - rv];
10677c478bd9Sstevel@tonic-gate 	thseq = ntohl(tcp->th_seq);
10687c478bd9Sstevel@tonic-gate 	thack = ntohl(tcp->th_ack);
10697c478bd9Sstevel@tonic-gate 
1070ab25eeb5Syz #ifdef __sgi
1071ab25eeb5Syz 	mlen = fin->fin_plen - off;
1072ab25eeb5Syz #else
10737c478bd9Sstevel@tonic-gate 	mlen = MSGDSIZE(m) - off;
1074ab25eeb5Syz #endif
1075*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_debug > 4)
1076ab25eeb5Syz 		printf("ippr_ftp_process: mlen %d\n", mlen);
1077ab25eeb5Syz 
10787c478bd9Sstevel@tonic-gate 	if (mlen <= 0) {
10797c478bd9Sstevel@tonic-gate 		if ((tcp->th_flags & TH_OPENING) == TH_OPENING) {
10807c478bd9Sstevel@tonic-gate 			f->ftps_seq[0] = thseq + 1;
10817c478bd9Sstevel@tonic-gate 			t->ftps_seq[0] = thack;
10827c478bd9Sstevel@tonic-gate 		}
10837c478bd9Sstevel@tonic-gate 		return 0;
10847c478bd9Sstevel@tonic-gate 	}
10857c478bd9Sstevel@tonic-gate 	aps = nat->nat_aps;
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 	sel = aps->aps_sel[1 - rv];
10887c478bd9Sstevel@tonic-gate 	sel2 = aps->aps_sel[rv];
10897c478bd9Sstevel@tonic-gate 	if (rv == 0) {
10907c478bd9Sstevel@tonic-gate 		seqoff = aps->aps_seqoff[sel];
10917c478bd9Sstevel@tonic-gate 		if (aps->aps_seqmin[sel] > seqoff + thseq)
10927c478bd9Sstevel@tonic-gate 			seqoff = aps->aps_seqoff[!sel];
10937c478bd9Sstevel@tonic-gate 		ackoff = aps->aps_ackoff[sel2];
10947c478bd9Sstevel@tonic-gate 		if (aps->aps_ackmin[sel2] > ackoff + thack)
10957c478bd9Sstevel@tonic-gate 			ackoff = aps->aps_ackoff[!sel2];
10967c478bd9Sstevel@tonic-gate 	} else {
10977c478bd9Sstevel@tonic-gate 		seqoff = aps->aps_ackoff[sel];
1098*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 2)
1099ab25eeb5Syz 			printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1100ab25eeb5Syz 			       aps->aps_ackmin[sel]);
11017c478bd9Sstevel@tonic-gate 		if (aps->aps_ackmin[sel] > seqoff + thseq)
11027c478bd9Sstevel@tonic-gate 			seqoff = aps->aps_ackoff[!sel];
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 		ackoff = aps->aps_seqoff[sel2];
1105*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 2)
1106ab25eeb5Syz 			printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1107ab25eeb5Syz 			       aps->aps_seqmin[sel2]);
11087c478bd9Sstevel@tonic-gate 		if (ackoff > 0) {
11097c478bd9Sstevel@tonic-gate 			if (aps->aps_seqmin[sel2] > ackoff + thack)
11107c478bd9Sstevel@tonic-gate 				ackoff = aps->aps_seqoff[!sel2];
11117c478bd9Sstevel@tonic-gate 		} else {
11127c478bd9Sstevel@tonic-gate 			if (aps->aps_seqmin[sel2] > thack)
11137c478bd9Sstevel@tonic-gate 				ackoff = aps->aps_seqoff[!sel2];
11147c478bd9Sstevel@tonic-gate 		}
11157c478bd9Sstevel@tonic-gate 	}
1116*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_debug > 2) {
1117ab25eeb5Syz 		printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1118ab25eeb5Syz 		       rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1119ab25eeb5Syz 		       thack, ackoff, mlen, fin->fin_plen, off);
1120ab25eeb5Syz 		printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1121ab25eeb5Syz 		       aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1122ab25eeb5Syz 		       aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1123ab25eeb5Syz 		printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1124ab25eeb5Syz 		       aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1125ab25eeb5Syz 		       aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1126ab25eeb5Syz 	}
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate 	/*
11297c478bd9Sstevel@tonic-gate 	 * XXX - Ideally, this packet should get dropped because we now know
11307c478bd9Sstevel@tonic-gate 	 * that it is out of order (and there is no real danger in doing so
11317c478bd9Sstevel@tonic-gate 	 * apart from causing packets to go through here ordered).
11327c478bd9Sstevel@tonic-gate 	 */
1133*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_debug > 2) {
1134ab25eeb5Syz 		printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1135ab25eeb5Syz 		       rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1136ab25eeb5Syz 	}
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	ok = 0;
11397c478bd9Sstevel@tonic-gate 	if (t->ftps_seq[0] == 0) {
11407c478bd9Sstevel@tonic-gate 		t->ftps_seq[0] = thack;
11417c478bd9Sstevel@tonic-gate 		ok = 1;
11427c478bd9Sstevel@tonic-gate 	} else {
11437c478bd9Sstevel@tonic-gate 		if (ackoff == 0) {
11447c478bd9Sstevel@tonic-gate 			if (t->ftps_seq[0] == thack)
11457c478bd9Sstevel@tonic-gate 				ok = 1;
11467c478bd9Sstevel@tonic-gate 			else if (t->ftps_seq[1] == thack) {
11477c478bd9Sstevel@tonic-gate 				t->ftps_seq[0] = thack;
11487c478bd9Sstevel@tonic-gate 				ok = 1;
11497c478bd9Sstevel@tonic-gate 			}
11507c478bd9Sstevel@tonic-gate 		} else {
11517c478bd9Sstevel@tonic-gate 			if (t->ftps_seq[0] + ackoff == thack)
11527c478bd9Sstevel@tonic-gate 				ok = 1;
11537c478bd9Sstevel@tonic-gate 			else if (t->ftps_seq[0] == thack + ackoff)
11547c478bd9Sstevel@tonic-gate 				ok = 1;
11557c478bd9Sstevel@tonic-gate 			else if (t->ftps_seq[1] + ackoff == thack) {
11567c478bd9Sstevel@tonic-gate 				t->ftps_seq[0] = thack - ackoff;
11577c478bd9Sstevel@tonic-gate 				ok = 1;
11587c478bd9Sstevel@tonic-gate 			} else if (t->ftps_seq[1] == thack + ackoff) {
11597c478bd9Sstevel@tonic-gate 				t->ftps_seq[0] = thack - ackoff;
11607c478bd9Sstevel@tonic-gate 				ok = 1;
11617c478bd9Sstevel@tonic-gate 			}
11627c478bd9Sstevel@tonic-gate 		}
11637c478bd9Sstevel@tonic-gate 	}
11647c478bd9Sstevel@tonic-gate 
1165*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_debug > 2) {
1166ab25eeb5Syz 		if (!ok)
1167ab25eeb5Syz 			printf("%s ok\n", "not");
1168ab25eeb5Syz 	}
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate 	if (!mlen) {
11717c478bd9Sstevel@tonic-gate 		if (t->ftps_seq[0] + ackoff != thack) {
1172*f4b3ec61Sdh 			if (ifsftp->ippr_ftp_debug > 1) {
1173ab25eeb5Syz 				printf("%s:seq[0](%x) + (%x) != (%x)\n",
1174ab25eeb5Syz 				       "ippr_ftp_process", t->ftps_seq[0],
1175ab25eeb5Syz 				       ackoff, thack);
1176ab25eeb5Syz 			}
11777c478bd9Sstevel@tonic-gate 			return APR_ERR(1);
11787c478bd9Sstevel@tonic-gate 		}
11797c478bd9Sstevel@tonic-gate 
1180*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 2) {
1181ab25eeb5Syz 			printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n",
1182ab25eeb5Syz 				f->ftps_seq[0], f->ftps_seq[1]);
1183ab25eeb5Syz 		}
1184ab25eeb5Syz 
11857c478bd9Sstevel@tonic-gate 		if (tcp->th_flags & TH_FIN) {
11867c478bd9Sstevel@tonic-gate 			if (thseq == f->ftps_seq[1]) {
11877c478bd9Sstevel@tonic-gate 				f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
11887c478bd9Sstevel@tonic-gate 				f->ftps_seq[1] = thseq + 1 - seqoff;
11897c478bd9Sstevel@tonic-gate 			} else {
1190*f4b3ec61Sdh 				if (ifsftp->ippr_ftp_debug > 1) {
1191ab25eeb5Syz 					printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1192ab25eeb5Syz 					       thseq, seqoff, f->ftps_seq[0]);
1193ab25eeb5Syz 				}
11947c478bd9Sstevel@tonic-gate 				return APR_ERR(1);
11957c478bd9Sstevel@tonic-gate 			}
11967c478bd9Sstevel@tonic-gate 		}
11977c478bd9Sstevel@tonic-gate 		f->ftps_len = 0;
11987c478bd9Sstevel@tonic-gate 		return 0;
11997c478bd9Sstevel@tonic-gate 	}
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate 	ok = 0;
12027c478bd9Sstevel@tonic-gate 	if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
12037c478bd9Sstevel@tonic-gate 		ok = 1;
12047c478bd9Sstevel@tonic-gate 	/*
12057c478bd9Sstevel@tonic-gate 	 * Retransmitted data packet.
12067c478bd9Sstevel@tonic-gate 	 */
12077c478bd9Sstevel@tonic-gate 	} else if ((thseq + mlen == f->ftps_seq[0]) ||
12087c478bd9Sstevel@tonic-gate 		   (thseq + mlen == f->ftps_seq[1])) {
12097c478bd9Sstevel@tonic-gate 		ok = 1;
12107c478bd9Sstevel@tonic-gate 	}
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 	if (ok == 0) {
12137c478bd9Sstevel@tonic-gate 		inc = thseq - f->ftps_seq[0];
1214*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 1) {
1215ab25eeb5Syz 			printf("inc %d sel %d rv %d\n", inc, sel, rv);
1216ab25eeb5Syz 			printf("th_seq %x ftps_seq %x/%x\n",
1217ab25eeb5Syz 			       thseq, f->ftps_seq[0], f->ftps_seq[1]);
1218ab25eeb5Syz 			printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1219ab25eeb5Syz 			       aps->aps_ackoff[sel]);
1220ab25eeb5Syz 			printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1221ab25eeb5Syz 			       aps->aps_seqoff[sel]);
1222ab25eeb5Syz 		}
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 		return APR_ERR(1);
12257c478bd9Sstevel@tonic-gate 	}
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 	inc = 0;
12287c478bd9Sstevel@tonic-gate 	rptr = f->ftps_rptr;
12297c478bd9Sstevel@tonic-gate 	wptr = f->ftps_wptr;
12307c478bd9Sstevel@tonic-gate 	f->ftps_seq[0] = thseq;
12317c478bd9Sstevel@tonic-gate 	f->ftps_seq[1] = f->ftps_seq[0] + mlen;
12327c478bd9Sstevel@tonic-gate 	f->ftps_len = mlen;
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 	while (mlen > 0) {
1235ab25eeb5Syz 		len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
12367c478bd9Sstevel@tonic-gate 		COPYDATA(m, off, len, wptr);
12377c478bd9Sstevel@tonic-gate 		mlen -= len;
12387c478bd9Sstevel@tonic-gate 		off += len;
12397c478bd9Sstevel@tonic-gate 		wptr += len;
1240ab25eeb5Syz 
1241*f4b3ec61Sdh 		if (ifsftp->ippr_ftp_debug > 3)
1242ab25eeb5Syz 			printf("%s:len %d/%d off %d wptr %lx junk %d [%*s]\n",
1243ab25eeb5Syz 			       "ippr_ftp_process",
1244ab25eeb5Syz 			       len, mlen, off, (u_long)wptr, f->ftps_junk,
1245ab25eeb5Syz 			       len, rptr);
1246ab25eeb5Syz 
12477c478bd9Sstevel@tonic-gate 		f->ftps_wptr = wptr;
1248ab25eeb5Syz 		if (f->ftps_junk != 0) {
1249ab25eeb5Syz 			i = f->ftps_junk;
12507c478bd9Sstevel@tonic-gate 			f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1251*f4b3ec61Sdh 						      wptr - rptr, ifsftp);
12527c478bd9Sstevel@tonic-gate 
1253*f4b3ec61Sdh 			if (ifsftp->ippr_ftp_debug > 5)
1254ab25eeb5Syz 				printf("%s:junk %d -> %d\n",
1255ab25eeb5Syz 				       "ippr_ftp_process", i, f->ftps_junk);
1256ab25eeb5Syz 
1257ab25eeb5Syz 			if (f->ftps_junk != 0) {
1258ab25eeb5Syz 				if (wptr - rptr == sizeof(f->ftps_buf)) {
1259*f4b3ec61Sdh 					if (ifsftp->ippr_ftp_debug > 4)
1260ab25eeb5Syz 						printf("%s:full buffer\n",
1261ab25eeb5Syz 						       "ippr_ftp_process");
1262ab25eeb5Syz 					f->ftps_rptr = f->ftps_buf;
1263ab25eeb5Syz 					f->ftps_wptr = f->ftps_buf;
1264ab25eeb5Syz 					rptr = f->ftps_rptr;
1265ab25eeb5Syz 					wptr = f->ftps_wptr;
1266ab25eeb5Syz 					/*
1267ab25eeb5Syz 					 * Because we throw away data here that
1268ab25eeb5Syz 					 * we would otherwise parse, set the
1269ab25eeb5Syz 					 * junk flag to indicate just ignore
1270ab25eeb5Syz 					 * any data upto the next CRLF.
1271ab25eeb5Syz 					 */
1272ab25eeb5Syz 					f->ftps_junk = 1;
1273ab25eeb5Syz 					continue;
1274ab25eeb5Syz 				}
1275ab25eeb5Syz 			}
1276ab25eeb5Syz 		}
1277ab25eeb5Syz 
12787c478bd9Sstevel@tonic-gate 		while ((f->ftps_junk == 0) && (wptr > rptr)) {
1279ab25eeb5Syz 			len = wptr - rptr;
1280*f4b3ec61Sdh 			f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len, ifsftp);
1281ab25eeb5Syz 
1282*f4b3ec61Sdh 			if (ifsftp->ippr_ftp_debug > 3) {
1283ab25eeb5Syz 				printf("%s=%d len %d rv %d ptr %lx/%lx ",
1284ab25eeb5Syz 				       "ippr_ftp_valid",
1285ab25eeb5Syz 				       f->ftps_junk, len, rv, (u_long)rptr,
1286ab25eeb5Syz 				       (u_long)wptr);
1287ab25eeb5Syz 				printf("buf [%*s]\n", len, rptr);
1288ab25eeb5Syz 			}
1289ab25eeb5Syz 
12907c478bd9Sstevel@tonic-gate 			if (f->ftps_junk == 0) {
12917c478bd9Sstevel@tonic-gate 				f->ftps_rptr = rptr;
12927c478bd9Sstevel@tonic-gate 				if (rv)
12937c478bd9Sstevel@tonic-gate 					inc += ippr_ftp_server(fin, ip, nat,
1294*f4b3ec61Sdh 							       ftp, len, ifsftp);
12957c478bd9Sstevel@tonic-gate 				else
12967c478bd9Sstevel@tonic-gate 					inc += ippr_ftp_client(fin, ip, nat,
1297*f4b3ec61Sdh 							       ftp, len, ifsftp);
12987c478bd9Sstevel@tonic-gate 				rptr = f->ftps_rptr;
12997c478bd9Sstevel@tonic-gate 				wptr = f->ftps_wptr;
13007c478bd9Sstevel@tonic-gate 			}
13017c478bd9Sstevel@tonic-gate 		}
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 		/*
13047c478bd9Sstevel@tonic-gate 		 * Off to a bad start so lets just forget about using the
13057c478bd9Sstevel@tonic-gate 		 * ftp proxy for this connection.
13067c478bd9Sstevel@tonic-gate 		 */
13077c478bd9Sstevel@tonic-gate 		if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) {
13087c478bd9Sstevel@tonic-gate 			/* f->ftps_seq[1] += inc; */
1309ab25eeb5Syz 
1310*f4b3ec61Sdh 			if (ifsftp->ippr_ftp_debug > 1)
1311ab25eeb5Syz 				printf("%s:cmds == 0 junk == 1\n",
1312ab25eeb5Syz 				       "ippr_ftp_process");
13137c478bd9Sstevel@tonic-gate 			return APR_ERR(2);
13147c478bd9Sstevel@tonic-gate 		}
13157c478bd9Sstevel@tonic-gate 
1316ab25eeb5Syz 		if ((f->ftps_junk != 0) && (rptr < wptr)) {
1317ab25eeb5Syz 			for (s = rptr; s < wptr; s++) {
1318ab25eeb5Syz 				if ((*s == '\r') && (s + 1 < wptr) &&
1319ab25eeb5Syz 				    (*(s + 1) == '\n')) {
1320ab25eeb5Syz 					rptr = s + 2;
1321ab25eeb5Syz 					f->ftps_junk = 0;
13227c478bd9Sstevel@tonic-gate 					break;
1323ab25eeb5Syz 				}
13247c478bd9Sstevel@tonic-gate 			}
13257c478bd9Sstevel@tonic-gate 		}
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 		if (rptr == wptr) {
13287c478bd9Sstevel@tonic-gate 			rptr = wptr = f->ftps_buf;
13297c478bd9Sstevel@tonic-gate 		} else {
1330ab25eeb5Syz 			/*
1331ab25eeb5Syz 			 * Compact the buffer back to the start.  The junk
1332ab25eeb5Syz 			 * flag should already be set and because we're not
1333ab25eeb5Syz 			 * throwing away any data, it is preserved from its
1334ab25eeb5Syz 			 * current state.
1335ab25eeb5Syz 			 */
1336ab25eeb5Syz 			if (rptr > f->ftps_buf) {
1337ab25eeb5Syz 				bcopy(rptr, f->ftps_buf, len);
1338ab25eeb5Syz 				wptr -= rptr - f->ftps_buf;
1339ab25eeb5Syz 				rptr = f->ftps_buf;
13407c478bd9Sstevel@tonic-gate 			}
13417c478bd9Sstevel@tonic-gate 		}
1342ab25eeb5Syz 		f->ftps_rptr = rptr;
1343ab25eeb5Syz 		f->ftps_wptr = wptr;
13447c478bd9Sstevel@tonic-gate 	}
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	/* f->ftps_seq[1] += inc; */
13477c478bd9Sstevel@tonic-gate 	if (tcp->th_flags & TH_FIN)
13487c478bd9Sstevel@tonic-gate 		f->ftps_seq[1]++;
1349*f4b3ec61Sdh 	if (ifsftp->ippr_ftp_debug > 3) {
1350ab25eeb5Syz #ifdef __sgi
1351ab25eeb5Syz 		mlen = fin->fin_plen;
1352ab25eeb5Syz #else
1353ab25eeb5Syz 		mlen = MSGDSIZE(m);
13547c478bd9Sstevel@tonic-gate #endif
1355ab25eeb5Syz 		mlen -= off;
1356ab25eeb5Syz 		printf("ftps_seq[1] = %x inc %d len %d\n",
1357ab25eeb5Syz 		       f->ftps_seq[1], inc, mlen);
1358ab25eeb5Syz 	}
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 	f->ftps_rptr = rptr;
13617c478bd9Sstevel@tonic-gate 	f->ftps_wptr = wptr;
13627c478bd9Sstevel@tonic-gate 	return APR_INC(inc);
13637c478bd9Sstevel@tonic-gate }
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate 
1366*f4b3ec61Sdh int ippr_ftp_out(fin, aps, nat, private)
13677c478bd9Sstevel@tonic-gate fr_info_t *fin;
13687c478bd9Sstevel@tonic-gate ap_session_t *aps;
13697c478bd9Sstevel@tonic-gate nat_t *nat;
1370*f4b3ec61Sdh void *private;
13717c478bd9Sstevel@tonic-gate {
13727c478bd9Sstevel@tonic-gate 	ftpinfo_t *ftp;
13737c478bd9Sstevel@tonic-gate 	int rev;
13747c478bd9Sstevel@tonic-gate 
13757c478bd9Sstevel@tonic-gate 	ftp = aps->aps_data;
13767c478bd9Sstevel@tonic-gate 	if (ftp == NULL)
13777c478bd9Sstevel@tonic-gate 		return 0;
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
13807c478bd9Sstevel@tonic-gate 	if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
13817c478bd9Sstevel@tonic-gate 		ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
13827c478bd9Sstevel@tonic-gate 
1383*f4b3ec61Sdh 	return ippr_ftp_process(fin, nat, ftp, rev, (ifs_ftppxy_t *)private);
13847c478bd9Sstevel@tonic-gate }
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 
1387*f4b3ec61Sdh int ippr_ftp_in(fin, aps, nat, private)
13887c478bd9Sstevel@tonic-gate fr_info_t *fin;
13897c478bd9Sstevel@tonic-gate ap_session_t *aps;
13907c478bd9Sstevel@tonic-gate nat_t *nat;
1391*f4b3ec61Sdh void *private;
13927c478bd9Sstevel@tonic-gate {
13937c478bd9Sstevel@tonic-gate 	ftpinfo_t *ftp;
13947c478bd9Sstevel@tonic-gate 	int rev;
13957c478bd9Sstevel@tonic-gate 
13967c478bd9Sstevel@tonic-gate 	ftp = aps->aps_data;
13977c478bd9Sstevel@tonic-gate 	if (ftp == NULL)
13987c478bd9Sstevel@tonic-gate 		return 0;
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
14017c478bd9Sstevel@tonic-gate 	if (ftp->ftp_side[rev].ftps_ifp == NULL)
14027c478bd9Sstevel@tonic-gate 		ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
14037c478bd9Sstevel@tonic-gate 
1404*f4b3ec61Sdh 	return ippr_ftp_process(fin, nat, ftp, 1 - rev, (ifs_ftppxy_t *)private);
14057c478bd9Sstevel@tonic-gate }
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate /*
14097c478bd9Sstevel@tonic-gate  * ippr_ftp_atoi - implement a version of atoi which processes numbers in
14107c478bd9Sstevel@tonic-gate  * pairs separated by commas (which are expected to be in the range 0 - 255),
14117c478bd9Sstevel@tonic-gate  * returning a 16 bit number combining either side of the , as the MSB and
14127c478bd9Sstevel@tonic-gate  * LSB.
14137c478bd9Sstevel@tonic-gate  */
14147c478bd9Sstevel@tonic-gate u_short ippr_ftp_atoi(ptr)
14157c478bd9Sstevel@tonic-gate char **ptr;
14167c478bd9Sstevel@tonic-gate {
14177c478bd9Sstevel@tonic-gate 	register char *s = *ptr, c;
14187c478bd9Sstevel@tonic-gate 	register u_char i = 0, j = 0;
14197c478bd9Sstevel@tonic-gate 
1420ab25eeb5Syz 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
14217c478bd9Sstevel@tonic-gate 		i *= 10;
14227c478bd9Sstevel@tonic-gate 		i += c - '0';
14237c478bd9Sstevel@tonic-gate 	}
14247c478bd9Sstevel@tonic-gate 	if (c != ',') {
14257c478bd9Sstevel@tonic-gate 		*ptr = NULL;
14267c478bd9Sstevel@tonic-gate 		return 0;
14277c478bd9Sstevel@tonic-gate 	}
1428ab25eeb5Syz 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
14297c478bd9Sstevel@tonic-gate 		j *= 10;
14307c478bd9Sstevel@tonic-gate 		j += c - '0';
14317c478bd9Sstevel@tonic-gate 	}
14327c478bd9Sstevel@tonic-gate 	*ptr = s;
14337c478bd9Sstevel@tonic-gate 	i &= 0xff;
14347c478bd9Sstevel@tonic-gate 	j &= 0xff;
14357c478bd9Sstevel@tonic-gate 	return (i << 8) | j;
14367c478bd9Sstevel@tonic-gate }
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 
1439*f4b3ec61Sdh int ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp)
14407c478bd9Sstevel@tonic-gate fr_info_t *fin;
14417c478bd9Sstevel@tonic-gate ip_t *ip;
14427c478bd9Sstevel@tonic-gate nat_t *nat;
14437c478bd9Sstevel@tonic-gate ftpside_t *f;
14447c478bd9Sstevel@tonic-gate int dlen;
1445*f4b3ec61Sdh ifs_ftppxy_t *ifsftp;
14467c478bd9Sstevel@tonic-gate {
14477c478bd9Sstevel@tonic-gate 	char newbuf[IPF_FTPBUFSZ];
14487c478bd9Sstevel@tonic-gate 	char *s;
14497c478bd9Sstevel@tonic-gate 	u_short ap = 0;
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate #define EPSV_REPLEN	33
14527c478bd9Sstevel@tonic-gate 	/*
14537c478bd9Sstevel@tonic-gate 	 * Check for EPSV reply message.
14547c478bd9Sstevel@tonic-gate 	 */
14557c478bd9Sstevel@tonic-gate 	if (dlen < IPF_MIN229LEN)
14567c478bd9Sstevel@tonic-gate 		return (0);
14577c478bd9Sstevel@tonic-gate 	else if (strncmp(f->ftps_rptr,
14587c478bd9Sstevel@tonic-gate 			 "229 Entering Extended Passive Mode", EPSV_REPLEN))
14597c478bd9Sstevel@tonic-gate 		return (0);
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate 	/*
14627c478bd9Sstevel@tonic-gate 	 * Skip the EPSV command + space
14637c478bd9Sstevel@tonic-gate 	 */
14647c478bd9Sstevel@tonic-gate 	s = f->ftps_rptr + 33;
1465ab25eeb5Syz 	while (*s && !ISDIGIT(*s))
14667c478bd9Sstevel@tonic-gate 		s++;
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	/*
14697c478bd9Sstevel@tonic-gate 	 * As per RFC 2428, there are no addres components in the EPSV
14707c478bd9Sstevel@tonic-gate 	 * response.  So we'll go straight to getting the port.
14717c478bd9Sstevel@tonic-gate 	 */
1472ab25eeb5Syz 	while (*s && ISDIGIT(*s)) {
14737c478bd9Sstevel@tonic-gate 		ap *= 10;
14747c478bd9Sstevel@tonic-gate 		ap += *s++ - '0';
14757c478bd9Sstevel@tonic-gate 	}
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate 	if (!s)
14787c478bd9Sstevel@tonic-gate 		return 0;
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 	if (*s == '|')
14817c478bd9Sstevel@tonic-gate 		s++;
14827c478bd9Sstevel@tonic-gate 	if (*s == ')')
14837c478bd9Sstevel@tonic-gate 		s++;
14847c478bd9Sstevel@tonic-gate 	if (*s == '\n')
14857c478bd9Sstevel@tonic-gate 		s--;
14867c478bd9Sstevel@tonic-gate 	/*
14877c478bd9Sstevel@tonic-gate 	 * check for CR-LF at the end.
14887c478bd9Sstevel@tonic-gate 	 */
14897c478bd9Sstevel@tonic-gate 	if ((*s == '\r') && (*(s + 1) == '\n')) {
14907c478bd9Sstevel@tonic-gate 		s += 2;
14917c478bd9Sstevel@tonic-gate 	} else
14927c478bd9Sstevel@tonic-gate 		return 0;
14937c478bd9Sstevel@tonic-gate 
1494ab25eeb5Syz #if defined(SNPRINTF) && defined(_KERNEL)
1495ab25eeb5Syz 	(void) SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1496ab25eeb5Syz 		 "229 Entering Extended Passive Mode", ap);
14977c478bd9Sstevel@tonic-gate #else
14987c478bd9Sstevel@tonic-gate 	(void) sprintf(newbuf, "%s (|||%u|)\r\n",
14997c478bd9Sstevel@tonic-gate 		       "229 Entering Extended Passive Mode", ap);
15007c478bd9Sstevel@tonic-gate #endif
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate 	return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s,
1503*f4b3ec61Sdh 				  ip->ip_src.s_addr, ifsftp);
15047c478bd9Sstevel@tonic-gate }
1505