ip_ftp_pxy.c revision 145522
1145522Sdarrenr/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 145522 2005-04-25 18:43:14Z darrenr $	*/
2145522Sdarrenr
353642Sguido/*
4145522Sdarrenr * Copyright (C) 1997-2003 by Darren Reed
5145522Sdarrenr *
6145522Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
7145522Sdarrenr *
853642Sguido * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
953642Sguido * code.
1072006Sdarrenr *
1157126Sguido * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 145522 2005-04-25 18:43:14Z darrenr $
12145522Sdarrenr * Id: ip_ftp_pxy.c,v 2.88.2.15 2005/03/19 19:38:10 darrenr Exp
1353642Sguido */
1453642Sguido
1553642Sguido#define	IPF_FTP_PROXY
1653642Sguido
1753642Sguido#define	IPF_MINPORTLEN	18
1853642Sguido#define	IPF_MAXPORTLEN	30
1953642Sguido#define	IPF_MIN227LEN	39
2053642Sguido#define	IPF_MAX227LEN	51
21145522Sdarrenr#define	IPF_MIN229LEN	47
22145522Sdarrenr#define	IPF_MAX229LEN	51
2353642Sguido
2480482Sdarrenr#define	FTPXY_GO	0
2580482Sdarrenr#define	FTPXY_INIT	1
2680482Sdarrenr#define	FTPXY_USER_1	2
2780482Sdarrenr#define	FTPXY_USOK_1	3
2880482Sdarrenr#define	FTPXY_PASS_1	4
2980482Sdarrenr#define	FTPXY_PAOK_1	5
3080482Sdarrenr#define	FTPXY_AUTH_1	6
3180482Sdarrenr#define	FTPXY_AUOK_1	7
3280482Sdarrenr#define	FTPXY_ADAT_1	8
3380482Sdarrenr#define	FTPXY_ADOK_1	9
3480482Sdarrenr#define	FTPXY_ACCT_1	10
3580482Sdarrenr#define	FTPXY_ACOK_1	11
3680482Sdarrenr#define	FTPXY_USER_2	12
3780482Sdarrenr#define	FTPXY_USOK_2	13
3880482Sdarrenr#define	FTPXY_PASS_2	14
3980482Sdarrenr#define	FTPXY_PAOK_2	15
4053642Sguido
41110916Sdarrenr/*
42110916Sdarrenr * Values for FTP commands.  Numerics cover 0-999
43110916Sdarrenr */
44110916Sdarrenr#define	FTPXY_C_PASV	1000
45110916Sdarrenr
4660857Sdarrenrint ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
4760857Sdarrenrint ippr_ftp_complete __P((char *, size_t));
48145522Sdarrenrint ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *));
4953642Sguidoint ippr_ftp_init __P((void));
50145522Sdarrenrvoid ippr_ftp_fini __P((void));
51145522Sdarrenrint ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *));
52145522Sdarrenrint ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *));
53110916Sdarrenrint ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
54145522Sdarrenrint ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
5560857Sdarrenrint ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
56145522Sdarrenrint ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int));
5760857Sdarrenrint ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
58110916Sdarrenrint ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t));
59110916Sdarrenrint ippr_ftp_server_valid __P((ftpside_t *, char *, size_t));
60110916Sdarrenrint ippr_ftp_client_valid __P((ftpside_t *, char *, size_t));
6160857Sdarrenru_short ippr_ftp_atoi __P((char **));
62145522Sdarrenrint ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *,
63145522Sdarrenr			    u_int, char *, char *, u_int));
6453642Sguido
65145522Sdarrenr
66145522Sdarrenrint	ftp_proxy_init = 0;
6760857Sdarrenrint	ippr_ftp_pasvonly = 0;
68145522Sdarrenrint	ippr_ftp_insecure = 0;	/* Do not require logins before transfers */
69145522Sdarrenrint	ippr_ftp_pasvrdr = 0;
70145522Sdarrenrint	ippr_ftp_forcepasv = 0;	/* PASV must be last command prior to 227 */
71145522Sdarrenr#if defined(_KERNEL)
72145522Sdarrenrint	ippr_ftp_debug = 0;
73145522Sdarrenr#else
74145522Sdarrenrint	ippr_ftp_debug = 2;
75145522Sdarrenr#endif
76145522Sdarrenr/*
77145522Sdarrenr * 1 - security
78145522Sdarrenr * 2 - errors
79145522Sdarrenr * 3 - error debugging
80145522Sdarrenr * 4 - parsing errors
81145522Sdarrenr * 5 - parsing info
82145522Sdarrenr * 6 - parsing debug
83145522Sdarrenr */
8453642Sguido
85145522Sdarrenrstatic	frentry_t	ftppxyfr;
86145522Sdarrenrstatic	ipftuneable_t	ftptune = {
87145522Sdarrenr	{ &ippr_ftp_debug },
88145522Sdarrenr	"ippr_ftp_debug",
89145522Sdarrenr	0,
90145522Sdarrenr	10,
91145522Sdarrenr	sizeof(ippr_ftp_debug),
92145522Sdarrenr	0,
93145522Sdarrenr	NULL
94145522Sdarrenr};
9553642Sguido
96145522Sdarrenr
9753642Sguido/*
9853642Sguido * Initialize local structures.
9953642Sguido */
10053642Sguidoint ippr_ftp_init()
10153642Sguido{
10292685Sdarrenr	bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
10392685Sdarrenr	ftppxyfr.fr_ref = 1;
10492685Sdarrenr	ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
105145522Sdarrenr	MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex");
106145522Sdarrenr	ftp_proxy_init = 1;
107145522Sdarrenr	(void) fr_addipftune(&ftptune);
108145522Sdarrenr
10953642Sguido	return 0;
11053642Sguido}
11153642Sguido
11253642Sguido
113145522Sdarrenrvoid ippr_ftp_fini()
114145522Sdarrenr{
115145522Sdarrenr	(void) fr_delipftune(&ftptune);
116145522Sdarrenr
117145522Sdarrenr	if (ftp_proxy_init == 1) {
118145522Sdarrenr		MUTEX_DESTROY(&ftppxyfr.fr_lock);
119145522Sdarrenr		ftp_proxy_init = 0;
120145522Sdarrenr	}
121145522Sdarrenr}
122145522Sdarrenr
123145522Sdarrenr
124145522Sdarrenrint ippr_ftp_new(fin, aps, nat)
12560857Sdarrenrfr_info_t *fin;
12660857Sdarrenrap_session_t *aps;
12760857Sdarrenrnat_t *nat;
12853642Sguido{
12960857Sdarrenr	ftpinfo_t *ftp;
13060857Sdarrenr	ftpside_t *f;
13153642Sguido
13260857Sdarrenr	KMALLOC(ftp, ftpinfo_t *);
13360857Sdarrenr	if (ftp == NULL)
13460857Sdarrenr		return -1;
135145522Sdarrenr
136145522Sdarrenr	fin = fin;	/* LINT */
137145522Sdarrenr	nat = nat;	/* LINT */
138145522Sdarrenr
13960857Sdarrenr	aps->aps_data = ftp;
14060857Sdarrenr	aps->aps_psiz = sizeof(ftpinfo_t);
14160857Sdarrenr
14260857Sdarrenr	bzero((char *)ftp, sizeof(*ftp));
14360857Sdarrenr	f = &ftp->ftp_side[0];
14460857Sdarrenr	f->ftps_rptr = f->ftps_buf;
14560857Sdarrenr	f->ftps_wptr = f->ftps_buf;
14660857Sdarrenr	f = &ftp->ftp_side[1];
14760857Sdarrenr	f->ftps_rptr = f->ftps_buf;
14860857Sdarrenr	f->ftps_wptr = f->ftps_buf;
14980482Sdarrenr	ftp->ftp_passok = FTPXY_INIT;
150145522Sdarrenr	ftp->ftp_incok = 0;
15160857Sdarrenr	return 0;
15253642Sguido}
15353642Sguido
15453642Sguido
15560857Sdarrenrint ippr_ftp_port(fin, ip, nat, f, dlen)
15653642Sguidofr_info_t *fin;
15753642Sguidoip_t *ip;
15853642Sguidonat_t *nat;
15960857Sdarrenrftpside_t *f;
16060857Sdarrenrint dlen;
16153642Sguido{
16253642Sguido	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
16360857Sdarrenr	char newbuf[IPF_FTPBUFSZ], *s;
164145522Sdarrenr	struct in_addr swip, swip2;
16553642Sguido	u_int a1, a2, a3, a4;
166145522Sdarrenr	int inc, off, flags;
16792685Sdarrenr	u_short a5, a6, sp;
16860857Sdarrenr	size_t nlen, olen;
16953642Sguido	fr_info_t fi;
170145522Sdarrenr	nat_t *nat2;
17153642Sguido	mb_t *m;
17253642Sguido
173145522Sdarrenr	m = fin->fin_m;
17453642Sguido	tcp = (tcphdr_t *)fin->fin_dp;
175145522Sdarrenr	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
176145522Sdarrenr
17760857Sdarrenr	/*
17860857Sdarrenr	 * Check for client sending out PORT message.
17960857Sdarrenr	 */
180110916Sdarrenr	if (dlen < IPF_MINPORTLEN) {
181145522Sdarrenr		if (ippr_ftp_debug > 1)
182145522Sdarrenr			printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
183145522Sdarrenr			       dlen);
18453642Sguido		return 0;
185110916Sdarrenr	}
18660857Sdarrenr	/*
18753642Sguido	 * Skip the PORT command + space
18853642Sguido	 */
18960857Sdarrenr	s = f->ftps_rptr + 5;
19053642Sguido	/*
19153642Sguido	 * Pick out the address components, two at a time.
19253642Sguido	 */
19360857Sdarrenr	a1 = ippr_ftp_atoi(&s);
194110916Sdarrenr	if (s == NULL) {
195145522Sdarrenr		if (ippr_ftp_debug > 1)
196145522Sdarrenr			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1);
19753642Sguido		return 0;
198110916Sdarrenr	}
19960857Sdarrenr	a2 = ippr_ftp_atoi(&s);
200110916Sdarrenr	if (s == NULL) {
201145522Sdarrenr		if (ippr_ftp_debug > 1)
202145522Sdarrenr			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2);
20353642Sguido		return 0;
204110916Sdarrenr	}
205145522Sdarrenr
20653642Sguido	/*
207145522Sdarrenr	 * Check that IP address in the PORT/PASV reply is the same as the
20853642Sguido	 * sender of the command - prevents using PORT for port scanning.
20953642Sguido	 */
21053642Sguido	a1 <<= 16;
21153642Sguido	a1 |= a2;
212145522Sdarrenr	if (((nat->nat_dir == NAT_OUTBOUND) &&
213145522Sdarrenr	     (a1 != ntohl(nat->nat_inip.s_addr))) ||
214145522Sdarrenr	    ((nat->nat_dir == NAT_INBOUND) &&
215145522Sdarrenr	     (a1 != ntohl(nat->nat_oip.s_addr)))) {
216145522Sdarrenr		if (ippr_ftp_debug > 0)
217145522Sdarrenr			printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1");
218145522Sdarrenr		return APR_ERR(1);
219110916Sdarrenr	}
22053642Sguido
22160857Sdarrenr	a5 = ippr_ftp_atoi(&s);
222110916Sdarrenr	if (s == NULL) {
223145522Sdarrenr		if (ippr_ftp_debug > 1)
224145522Sdarrenr			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3);
22553642Sguido		return 0;
226110916Sdarrenr	}
22753642Sguido	if (*s == ')')
22853642Sguido		s++;
22953642Sguido
23053642Sguido	/*
23153642Sguido	 * check for CR-LF at the end.
23253642Sguido	 */
23353642Sguido	if (*s == '\n')
23453642Sguido		s--;
23553642Sguido	if ((*s == '\r') && (*(s + 1) == '\n')) {
23653642Sguido		s += 2;
23753642Sguido		a6 = a5 & 0xff;
238110916Sdarrenr	} else {
239145522Sdarrenr		if (ippr_ftp_debug > 1)
240145522Sdarrenr			printf("ippr_ftp_port:missing %s\n", "cr-lf");
24153642Sguido		return 0;
242110916Sdarrenr	}
243145522Sdarrenr
24453642Sguido	a5 >>= 8;
24567614Sdarrenr	a5 &= 0xff;
246145522Sdarrenr	sp = a5 << 8 | a6;
24753642Sguido	/*
248145522Sdarrenr	 * Don't allow the PORT command to specify a port < 1024 due to
249145522Sdarrenr	 * security crap.
250145522Sdarrenr	 */
251145522Sdarrenr	if (sp < 1024) {
252145522Sdarrenr		if (ippr_ftp_debug > 0)
253145522Sdarrenr			printf("ippr_ftp_port:sp(%d) < 1024\n", sp);
254145522Sdarrenr		return 0;
255145522Sdarrenr	}
256145522Sdarrenr	/*
25753642Sguido	 * Calculate new address parts for PORT command
25853642Sguido	 */
259145522Sdarrenr	if (nat->nat_dir == NAT_INBOUND)
260145522Sdarrenr		a1 = ntohl(nat->nat_oip.s_addr);
261145522Sdarrenr	else
262145522Sdarrenr		a1 = ntohl(ip->ip_src.s_addr);
26353642Sguido	a2 = (a1 >> 16) & 0xff;
26453642Sguido	a3 = (a1 >> 8) & 0xff;
26553642Sguido	a4 = a1 & 0xff;
26653642Sguido	a1 >>= 24;
26760857Sdarrenr	olen = s - f->ftps_rptr;
26892685Sdarrenr	/* DO NOT change this to snprintf! */
269145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL)
270145522Sdarrenr	SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
271145522Sdarrenr		 "PORT", a1, a2, a3, a4, a5, a6);
272130886Sdarrenr#else
27355929Sguido	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
27453642Sguido		       "PORT", a1, a2, a3, a4, a5, a6);
275130886Sdarrenr#endif
27653642Sguido
27753642Sguido	nlen = strlen(newbuf);
27853642Sguido	inc = nlen - olen;
279110916Sdarrenr	if ((inc + ip->ip_len) > 65535) {
280145522Sdarrenr		if (ippr_ftp_debug > 0)
281145522Sdarrenr			printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n",
282145522Sdarrenr			       inc);
28360857Sdarrenr		return 0;
284110916Sdarrenr	}
28560857Sdarrenr
28695563Sdarrenr#if !defined(_KERNEL)
287145522Sdarrenr	bcopy(newbuf, MTOD(m, char *) + off, nlen);
28895563Sdarrenr#else
289145522Sdarrenr# if defined(MENTAT)
29053642Sguido	if (inc < 0)
291145522Sdarrenr		(void)adjmsg(m, inc);
292145522Sdarrenr# else /* defined(MENTAT) */
293145522Sdarrenr	/*
294145522Sdarrenr	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
295145522Sdarrenr	 * mean remove -len bytes from the end of the packet.
296145522Sdarrenr	 * The mbuf chain will be extended if necessary by m_copyback().
297145522Sdarrenr	 */
298145522Sdarrenr	if (inc < 0)
29953642Sguido		m_adj(m, inc);
300145522Sdarrenr# endif /* defined(MENTAT) */
301145522Sdarrenr#endif /* !defined(_KERNEL) */
302145522Sdarrenr	COPYBACK(m, off, nlen, newbuf);
303145522Sdarrenr
30453642Sguido	if (inc != 0) {
30553642Sguido		ip->ip_len += inc;
306145522Sdarrenr		fin->fin_dlen += inc;
307145522Sdarrenr		fin->fin_plen += inc;
30853642Sguido	}
30953642Sguido
31053642Sguido	/*
31153642Sguido	 * The server may not make the connection back from port 20, but
31253642Sguido	 * it is the most likely so use it here to check for a conflicting
31353642Sguido	 * mapping.
31453642Sguido	 */
31592685Sdarrenr	bcopy((char *)fin, (char *)&fi, sizeof(fi));
316145522Sdarrenr	fi.fin_state = NULL;
317145522Sdarrenr	fi.fin_nat = NULL;
318145522Sdarrenr	fi.fin_flx |= FI_IGNORE;
31992685Sdarrenr	fi.fin_data[0] = sp;
32092685Sdarrenr	fi.fin_data[1] = fin->fin_data[1] - 1;
321145522Sdarrenr	/*
322145522Sdarrenr	 * Add skeleton NAT entry for connection which will come back the
323145522Sdarrenr	 * other way.
324145522Sdarrenr	 */
325145522Sdarrenr	if (nat->nat_dir == NAT_OUTBOUND)
326145522Sdarrenr		nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
327145522Sdarrenr				     nat->nat_inip, nat->nat_oip);
328145522Sdarrenr	else
329145522Sdarrenr		nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
330145522Sdarrenr				    nat->nat_inip, nat->nat_oip);
331145522Sdarrenr	if (nat2 == NULL) {
33260857Sdarrenr		int slen;
33360857Sdarrenr
33460857Sdarrenr		slen = ip->ip_len;
33560857Sdarrenr		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
33653642Sguido		bzero((char *)tcp2, sizeof(*tcp2));
33753642Sguido		tcp2->th_win = htons(8192);
33892685Sdarrenr		tcp2->th_sport = htons(sp);
339145522Sdarrenr		TCP_OFF_A(tcp2, 5);
34095563Sdarrenr		tcp2->th_flags = TH_SYN;
34153642Sguido		tcp2->th_dport = 0; /* XXX - don't specify remote port */
34253642Sguido		fi.fin_data[1] = 0;
34367853Sdarrenr		fi.fin_dlen = sizeof(*tcp2);
344145522Sdarrenr		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
34553642Sguido		fi.fin_dp = (char *)tcp2;
34692685Sdarrenr		fi.fin_fr = &ftppxyfr;
347145522Sdarrenr		fi.fin_out = nat->nat_dir;
348145522Sdarrenr		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
34953642Sguido		swip = ip->ip_src;
350145522Sdarrenr		swip2 = ip->ip_dst;
351145522Sdarrenr		if (nat->nat_dir == NAT_OUTBOUND) {
352145522Sdarrenr			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
353145522Sdarrenr			ip->ip_src = nat->nat_inip;
354145522Sdarrenr		} else if (nat->nat_dir == NAT_INBOUND) {
355145522Sdarrenr			fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
356145522Sdarrenr			ip->ip_src = nat->nat_oip;
35753642Sguido		}
358145522Sdarrenr
359145522Sdarrenr		flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
360145522Sdarrenr		if (nat->nat_dir == NAT_INBOUND)
361145522Sdarrenr			flags |= NAT_NOTRULEPORT;
362145522Sdarrenr		nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir);
363145522Sdarrenr
364145522Sdarrenr		if (nat2 != NULL) {
365145522Sdarrenr			(void) nat_proto(&fi, nat2, IPN_TCP);
366145522Sdarrenr			nat_update(&fi, nat2, nat->nat_ptr);
367145522Sdarrenr			fi.fin_ifp = NULL;
368145522Sdarrenr			if (nat->nat_dir == NAT_INBOUND) {
369145522Sdarrenr				fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
370145522Sdarrenr				ip->ip_dst = nat->nat_inip;
371145522Sdarrenr			}
372145522Sdarrenr			(void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT);
373145522Sdarrenr			if (fi.fin_state != NULL)
374145522Sdarrenr				fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
375145522Sdarrenr		}
37660857Sdarrenr		ip->ip_len = slen;
37753642Sguido		ip->ip_src = swip;
378145522Sdarrenr		ip->ip_dst = swip2;
379145522Sdarrenr	} else {
380145522Sdarrenr		ipstate_t *is;
381145522Sdarrenr
382145522Sdarrenr		nat_update(&fi, nat2, nat->nat_ptr);
383145522Sdarrenr		READ_ENTER(&ipf_state);
384145522Sdarrenr		is = nat2->nat_state;
385145522Sdarrenr		if (is != NULL) {
386145522Sdarrenr			MUTEX_ENTER(&is->is_lock);
387145522Sdarrenr			(void)fr_tcp_age(&is->is_sti, &fi, ips_tqtqb,
388145522Sdarrenr					 is->is_flags);
389145522Sdarrenr			MUTEX_EXIT(&is->is_lock);
390145522Sdarrenr		}
391145522Sdarrenr		RWLOCK_EXIT(&ipf_state);
39253642Sguido	}
393145522Sdarrenr	return APR_INC(inc);
39453642Sguido}
39553642Sguido
39653642Sguido
39760857Sdarrenrint ippr_ftp_client(fin, ip, nat, ftp, dlen)
39853642Sguidofr_info_t *fin;
39960857Sdarrenrnat_t *nat;
40060857Sdarrenrftpinfo_t *ftp;
40153642Sguidoip_t *ip;
40260857Sdarrenrint dlen;
40353642Sguido{
40463523Sdarrenr	char *rptr, *wptr, cmd[6], c;
40560857Sdarrenr	ftpside_t *f;
40663523Sdarrenr	int inc, i;
40760857Sdarrenr
40860857Sdarrenr	inc = 0;
40960857Sdarrenr	f = &ftp->ftp_side[0];
41060857Sdarrenr	rptr = f->ftps_rptr;
41160857Sdarrenr	wptr = f->ftps_wptr;
41260857Sdarrenr
41363523Sdarrenr	for (i = 0; (i < 5) && (i < dlen); i++) {
41463523Sdarrenr		c = rptr[i];
415145522Sdarrenr		if (ISALPHA(c)) {
416145522Sdarrenr			cmd[i] = TOUPPER(c);
41763523Sdarrenr		} else {
41863523Sdarrenr			cmd[i] = c;
41963523Sdarrenr		}
42063523Sdarrenr	}
42163523Sdarrenr	cmd[i] = '\0';
42263523Sdarrenr
42380482Sdarrenr	ftp->ftp_incok = 0;
42480482Sdarrenr	if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
42580482Sdarrenr		if (ftp->ftp_passok == FTPXY_ADOK_1 ||
42680482Sdarrenr		    ftp->ftp_passok == FTPXY_AUOK_1) {
42780482Sdarrenr			ftp->ftp_passok = FTPXY_USER_2;
42880482Sdarrenr			ftp->ftp_incok = 1;
42980482Sdarrenr		} else {
43080482Sdarrenr			ftp->ftp_passok = FTPXY_USER_1;
43180482Sdarrenr			ftp->ftp_incok = 1;
43280482Sdarrenr		}
43380482Sdarrenr	} else if (!strncmp(cmd, "AUTH ", 5)) {
43480482Sdarrenr		ftp->ftp_passok = FTPXY_AUTH_1;
43580482Sdarrenr		ftp->ftp_incok = 1;
43680482Sdarrenr	} else if (!strncmp(cmd, "PASS ", 5)) {
43780482Sdarrenr		if (ftp->ftp_passok == FTPXY_USOK_1) {
43880482Sdarrenr			ftp->ftp_passok = FTPXY_PASS_1;
43980482Sdarrenr			ftp->ftp_incok = 1;
44080482Sdarrenr		} else if (ftp->ftp_passok == FTPXY_USOK_2) {
44180482Sdarrenr			ftp->ftp_passok = FTPXY_PASS_2;
44280482Sdarrenr			ftp->ftp_incok = 1;
44380482Sdarrenr		}
44480482Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
44580482Sdarrenr		   !strncmp(cmd, "ADAT ", 5)) {
44680482Sdarrenr		ftp->ftp_passok = FTPXY_ADAT_1;
44780482Sdarrenr		ftp->ftp_incok = 1;
44892685Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
44992685Sdarrenr		    ftp->ftp_passok == FTPXY_PAOK_2) &&
45080482Sdarrenr		 !strncmp(cmd, "ACCT ", 5)) {
45180482Sdarrenr		ftp->ftp_passok = FTPXY_ACCT_1;
45280482Sdarrenr		ftp->ftp_incok = 1;
45380482Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly &&
45463523Sdarrenr		 !strncmp(cmd, "PORT ", 5)) {
45560857Sdarrenr		inc = ippr_ftp_port(fin, ip, nat, f, dlen);
45663523Sdarrenr	} else if (ippr_ftp_insecure && !ippr_ftp_pasvonly &&
45763523Sdarrenr		   !strncmp(cmd, "PORT ", 5)) {
45863523Sdarrenr		inc = ippr_ftp_port(fin, ip, nat, f, dlen);
45960857Sdarrenr	}
46060857Sdarrenr
46160857Sdarrenr	while ((*rptr++ != '\n') && (rptr < wptr))
46260857Sdarrenr		;
46360857Sdarrenr	f->ftps_rptr = rptr;
46460857Sdarrenr	return inc;
46553642Sguido}
46653642Sguido
46753642Sguido
468110916Sdarrenrint ippr_ftp_pasv(fin, ip, nat, ftp, dlen)
46953642Sguidofr_info_t *fin;
47053642Sguidoip_t *ip;
47153642Sguidonat_t *nat;
472110916Sdarrenrftpinfo_t *ftp;
47360857Sdarrenrint dlen;
47453642Sguido{
475145522Sdarrenr	u_int a1, a2, a3, a4, data_ip;
476145522Sdarrenr	char newbuf[IPF_FTPBUFSZ];
477145522Sdarrenr	char *s, *brackets[2];
478145522Sdarrenr	u_short a5, a6;
479110916Sdarrenr	ftpside_t *f;
48053642Sguido
481110916Sdarrenr	if (ippr_ftp_forcepasv != 0 &&
482110916Sdarrenr	    ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) {
483145522Sdarrenr		if (ippr_ftp_debug > 0)
484145522Sdarrenr			printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
485145522Sdarrenr			       ftp->ftp_side[0].ftps_cmds);
486110916Sdarrenr		return 0;
487110916Sdarrenr	}
488110916Sdarrenr
489110916Sdarrenr	f = &ftp->ftp_side[1];
490110916Sdarrenr
49180482Sdarrenr#define	PASV_REPLEN	24
49260857Sdarrenr	/*
49360857Sdarrenr	 * Check for PASV reply message.
49460857Sdarrenr	 */
495110916Sdarrenr	if (dlen < IPF_MIN227LEN) {
496145522Sdarrenr		if (ippr_ftp_debug > 1)
497145522Sdarrenr			printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
498145522Sdarrenr			       dlen);
49960857Sdarrenr		return 0;
500110916Sdarrenr	} else if (strncmp(f->ftps_rptr,
501110916Sdarrenr			   "227 Entering Passive Mod", PASV_REPLEN)) {
502145522Sdarrenr		if (ippr_ftp_debug > 0)
503145522Sdarrenr			printf("ippr_ftp_pasv:%d reply wrong\n", 227);
50460857Sdarrenr		return 0;
505110916Sdarrenr	}
50660857Sdarrenr
507145522Sdarrenr	brackets[0] = "";
508145522Sdarrenr	brackets[1] = "";
50953642Sguido	/*
510110916Sdarrenr	 * Skip the PASV reply + space
51153642Sguido	 */
51280482Sdarrenr	s = f->ftps_rptr + PASV_REPLEN;
513145522Sdarrenr	while (*s && !ISDIGIT(*s)) {
514145522Sdarrenr		if (*s == '(') {
515145522Sdarrenr			brackets[0] = "(";
516145522Sdarrenr			brackets[1] = ")";
517145522Sdarrenr		}
51853642Sguido		s++;
519145522Sdarrenr	}
520145522Sdarrenr
52153642Sguido	/*
52253642Sguido	 * Pick out the address components, two at a time.
52353642Sguido	 */
52460857Sdarrenr	a1 = ippr_ftp_atoi(&s);
525110916Sdarrenr	if (s == NULL) {
526145522Sdarrenr		if (ippr_ftp_debug > 1)
527145522Sdarrenr			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1);
52853642Sguido		return 0;
529110916Sdarrenr	}
53060857Sdarrenr	a2 = ippr_ftp_atoi(&s);
531110916Sdarrenr	if (s == NULL) {
532145522Sdarrenr		if (ippr_ftp_debug > 1)
533145522Sdarrenr			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2);
53453642Sguido		return 0;
535110916Sdarrenr	}
53653642Sguido
53753642Sguido	/*
538145522Sdarrenr	 * check that IP address in the PASV reply is the same as the
539145522Sdarrenr	 * sender of the command - prevents using PASV for port scanning.
54053642Sguido	 */
54153642Sguido	a1 <<= 16;
54253642Sguido	a1 |= a2;
543145522Sdarrenr
544145522Sdarrenr	if (((nat->nat_dir == NAT_INBOUND) &&
545145522Sdarrenr	     (a1 != ntohl(nat->nat_inip.s_addr))) ||
546145522Sdarrenr	    ((nat->nat_dir == NAT_OUTBOUND) &&
547145522Sdarrenr	     (a1 != ntohl(nat->nat_oip.s_addr)))) {
548145522Sdarrenr		if (ippr_ftp_debug > 0)
549145522Sdarrenr			printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1");
55053642Sguido		return 0;
551110916Sdarrenr	}
55253642Sguido
55360857Sdarrenr	a5 = ippr_ftp_atoi(&s);
554110916Sdarrenr	if (s == NULL) {
555145522Sdarrenr		if (ippr_ftp_debug > 1)
556145522Sdarrenr			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3);
55753642Sguido		return 0;
558110916Sdarrenr	}
55953642Sguido
56053642Sguido	if (*s == ')')
56153642Sguido		s++;
56280482Sdarrenr	if (*s == '.')
56380482Sdarrenr		s++;
56453642Sguido	if (*s == '\n')
56553642Sguido		s--;
56653642Sguido	/*
56753642Sguido	 * check for CR-LF at the end.
56853642Sguido	 */
56953642Sguido	if ((*s == '\r') && (*(s + 1) == '\n')) {
57053642Sguido		s += 2;
571110916Sdarrenr	} else {
572145522Sdarrenr		if (ippr_ftp_debug > 1)
573145522Sdarrenr			printf("ippr_ftp_pasv:missing %s", "cr-lf\n");
57453642Sguido		return 0;
575110916Sdarrenr	}
576145522Sdarrenr
577145522Sdarrenr	a6 = a5 & 0xff;
57853642Sguido	a5 >>= 8;
57953642Sguido	/*
58053642Sguido	 * Calculate new address parts for 227 reply
58153642Sguido	 */
582145522Sdarrenr	if (nat->nat_dir == NAT_INBOUND) {
583145522Sdarrenr		data_ip = nat->nat_outip.s_addr;
584145522Sdarrenr		a1 = ntohl(data_ip);
585145522Sdarrenr	} else
586145522Sdarrenr		data_ip = htonl(a1);
587145522Sdarrenr
58853642Sguido	a2 = (a1 >> 16) & 0xff;
58953642Sguido	a3 = (a1 >> 8) & 0xff;
59053642Sguido	a4 = a1 & 0xff;
59153642Sguido	a1 >>= 24;
592145522Sdarrenr
593145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL)
594145522Sdarrenr	SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
595145522Sdarrenr		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
596145522Sdarrenr		a5, a6, brackets[1]);
597145522Sdarrenr#else
598145522Sdarrenr	(void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
599145522Sdarrenr		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
600145522Sdarrenr		a5, a6, brackets[1]);
601145522Sdarrenr#endif
602145522Sdarrenr	return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6),
603145522Sdarrenr				  newbuf, s, data_ip);
604145522Sdarrenr}
605145522Sdarrenr
606145522Sdarrenrint ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip)
607145522Sdarrenrfr_info_t *fin;
608145522Sdarrenrip_t *ip;
609145522Sdarrenrnat_t *nat;
610145522Sdarrenrftpside_t *f;
611145522Sdarrenru_int port;
612145522Sdarrenrchar *newmsg;
613145522Sdarrenrchar *s;
614145522Sdarrenru_int data_ip;
615145522Sdarrenr{
616145522Sdarrenr	int inc, off, nflags, sflags;
617145522Sdarrenr	tcphdr_t *tcp, tcph, *tcp2;
618145522Sdarrenr	struct in_addr swip, swip2;
619145522Sdarrenr	struct in_addr data_addr;
620145522Sdarrenr	size_t nlen, olen;
621145522Sdarrenr	fr_info_t fi;
622145522Sdarrenr	nat_t *nat2;
623145522Sdarrenr	mb_t *m;
624145522Sdarrenr
625145522Sdarrenr	m = fin->fin_m;
626145522Sdarrenr	tcp = (tcphdr_t *)fin->fin_dp;
627145522Sdarrenr	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
628145522Sdarrenr
629145522Sdarrenr	data_addr.s_addr = data_ip;
630145522Sdarrenr	tcp2 = &tcph;
63160857Sdarrenr	inc = 0;
632145522Sdarrenr
633145522Sdarrenr
63460857Sdarrenr	olen = s - f->ftps_rptr;
635145522Sdarrenr	nlen = strlen(newmsg);
63653642Sguido	inc = nlen - olen;
637145522Sdarrenr	if ((inc + ip->ip_len) > 65535) {
638145522Sdarrenr		if (ippr_ftp_debug > 0)
639145522Sdarrenr			printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
640145522Sdarrenr			       inc);
64160857Sdarrenr		return 0;
642145522Sdarrenr	}
64360857Sdarrenr
64495563Sdarrenr#if !defined(_KERNEL)
645145522Sdarrenr	bcopy(newmsg, MTOD(m, char *) + off, nlen);
64695563Sdarrenr#else
647145522Sdarrenr# if defined(MENTAT)
64853642Sguido	if (inc < 0)
649145522Sdarrenr		(void)adjmsg(m, inc);
650145522Sdarrenr# else /* defined(MENTAT) */
651145522Sdarrenr	/*
652145522Sdarrenr	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
653145522Sdarrenr	 * mean remove -len bytes from the end of the packet.
654145522Sdarrenr	 * The mbuf chain will be extended if necessary by m_copyback().
655145522Sdarrenr	 */
656145522Sdarrenr	if (inc < 0)
65753642Sguido		m_adj(m, inc);
658145522Sdarrenr# endif /* defined(MENTAT) */
659145522Sdarrenr#endif /* !defined(_KERNEL) */
660145522Sdarrenr	COPYBACK(m, off, nlen, newmsg);
661145522Sdarrenr
66253642Sguido	if (inc != 0) {
66353642Sguido		ip->ip_len += inc;
664145522Sdarrenr		fin->fin_dlen += inc;
665145522Sdarrenr		fin->fin_plen += inc;
66653642Sguido	}
66753642Sguido
66853642Sguido	/*
66953642Sguido	 * Add skeleton NAT entry for connection which will come back the
67053642Sguido	 * other way.
67153642Sguido	 */
67292685Sdarrenr	bcopy((char *)fin, (char *)&fi, sizeof(fi));
673145522Sdarrenr	fi.fin_state = NULL;
674145522Sdarrenr	fi.fin_nat = NULL;
675145522Sdarrenr	fi.fin_flx |= FI_IGNORE;
67692685Sdarrenr	fi.fin_data[0] = 0;
677145522Sdarrenr	fi.fin_data[1] = port;
678145522Sdarrenr	nflags = IPN_TCP|SI_W_SPORT;
679145522Sdarrenr	if (ippr_ftp_pasvrdr && f->ftps_ifp)
680145522Sdarrenr		nflags |= SI_W_DPORT;
681145522Sdarrenr	if (nat->nat_dir == NAT_OUTBOUND)
682145522Sdarrenr		nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH,
683145522Sdarrenr				     nat->nat_p, nat->nat_inip, nat->nat_oip);
684145522Sdarrenr	else
685145522Sdarrenr		nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH,
686145522Sdarrenr				    nat->nat_p, nat->nat_inip, nat->nat_oip);
687145522Sdarrenr	if (nat2 == NULL) {
68860857Sdarrenr		int slen;
68960857Sdarrenr
69060857Sdarrenr		slen = ip->ip_len;
69160857Sdarrenr		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
69253642Sguido		bzero((char *)tcp2, sizeof(*tcp2));
69353642Sguido		tcp2->th_win = htons(8192);
69453642Sguido		tcp2->th_sport = 0;		/* XXX - fake it for nat_new */
695145522Sdarrenr		TCP_OFF_A(tcp2, 5);
69695563Sdarrenr		tcp2->th_flags = TH_SYN;
697145522Sdarrenr		fi.fin_data[1] = port;
69867853Sdarrenr		fi.fin_dlen = sizeof(*tcp2);
699145522Sdarrenr		tcp2->th_dport = htons(port);
70092685Sdarrenr		fi.fin_data[0] = 0;
70153642Sguido		fi.fin_dp = (char *)tcp2;
702145522Sdarrenr		fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
70392685Sdarrenr		fi.fin_fr = &ftppxyfr;
704145522Sdarrenr		fi.fin_out = nat->nat_dir;
705145522Sdarrenr		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
70653642Sguido		swip = ip->ip_src;
70753642Sguido		swip2 = ip->ip_dst;
708145522Sdarrenr		if (nat->nat_dir == NAT_OUTBOUND) {
709145522Sdarrenr			fi.fin_fi.fi_daddr = data_addr.s_addr;
710145522Sdarrenr			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
711145522Sdarrenr			ip->ip_dst = data_addr;
712145522Sdarrenr			ip->ip_src = nat->nat_inip;
713145522Sdarrenr		} else if (nat->nat_dir == NAT_INBOUND) {
714145522Sdarrenr			fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
715145522Sdarrenr			fi.fin_fi.fi_daddr = nat->nat_outip.s_addr;
716145522Sdarrenr			ip->ip_src = nat->nat_oip;
717145522Sdarrenr			ip->ip_dst = nat->nat_outip;
71853642Sguido		}
719145522Sdarrenr
720145522Sdarrenr		sflags = nflags;
721145522Sdarrenr		nflags |= NAT_SLAVE;
722145522Sdarrenr		if (nat->nat_dir == NAT_INBOUND)
723145522Sdarrenr			nflags |= NAT_NOTRULEPORT;
724145522Sdarrenr		nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir);
725145522Sdarrenr		if (nat2 != NULL) {
726145522Sdarrenr			(void) nat_proto(&fi, nat2, IPN_TCP);
727145522Sdarrenr			nat_update(&fi, nat2, nat->nat_ptr);
728145522Sdarrenr			fi.fin_ifp = NULL;
729145522Sdarrenr			if (nat->nat_dir == NAT_INBOUND) {
730145522Sdarrenr				fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
731145522Sdarrenr				ip->ip_dst = nat->nat_inip;
732145522Sdarrenr			}
733145522Sdarrenr			(void) fr_addstate(&fi, &nat2->nat_state, sflags);
734145522Sdarrenr			if (fi.fin_state != NULL)
735145522Sdarrenr				fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
736145522Sdarrenr		}
737145522Sdarrenr
73860857Sdarrenr		ip->ip_len = slen;
73953642Sguido		ip->ip_src = swip;
74053642Sguido		ip->ip_dst = swip2;
741145522Sdarrenr	} else {
742145522Sdarrenr		ipstate_t *is;
743145522Sdarrenr
744145522Sdarrenr		nat_update(&fi, nat2, nat->nat_ptr);
745145522Sdarrenr		READ_ENTER(&ipf_state);
746145522Sdarrenr		is = nat2->nat_state;
747145522Sdarrenr		if (is != NULL) {
748145522Sdarrenr			MUTEX_ENTER(&is->is_lock);
749145522Sdarrenr			(void)fr_tcp_age(&is->is_sti, &fi, ips_tqtqb,
750145522Sdarrenr					 is->is_flags);
751145522Sdarrenr			MUTEX_EXIT(&is->is_lock);
752145522Sdarrenr		}
753145522Sdarrenr		RWLOCK_EXIT(&ipf_state);
75453642Sguido	}
75553642Sguido	return inc;
75653642Sguido}
75753642Sguido
75853642Sguido
75960857Sdarrenrint ippr_ftp_server(fin, ip, nat, ftp, dlen)
76060857Sdarrenrfr_info_t *fin;
76160857Sdarrenrip_t *ip;
76260857Sdarrenrnat_t *nat;
76360857Sdarrenrftpinfo_t *ftp;
76460857Sdarrenrint dlen;
76560857Sdarrenr{
76660857Sdarrenr	char *rptr, *wptr;
76760857Sdarrenr	ftpside_t *f;
76860857Sdarrenr	int inc;
76960857Sdarrenr
77060857Sdarrenr	inc = 0;
77160857Sdarrenr	f = &ftp->ftp_side[1];
77260857Sdarrenr	rptr = f->ftps_rptr;
77360857Sdarrenr	wptr = f->ftps_wptr;
77460857Sdarrenr
775145522Sdarrenr	if (*rptr == ' ')
776145522Sdarrenr		goto server_cmd_ok;
777145522Sdarrenr	if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
778102520Sdarrenr		return 0;
77980482Sdarrenr	if (ftp->ftp_passok == FTPXY_GO) {
78080482Sdarrenr		if (!strncmp(rptr, "227 ", 4))
781110916Sdarrenr			inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
782145522Sdarrenr		else if (!strncmp(rptr, "229 ", 4))
783145522Sdarrenr			inc = ippr_ftp_epsv(fin, ip, nat, f, dlen);
78463523Sdarrenr	} else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
785110916Sdarrenr		inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
786145522Sdarrenr	} else if (ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
787145522Sdarrenr		inc = ippr_ftp_epsv(fin, ip, nat, f, dlen);
78880482Sdarrenr	} else if (*rptr == '5' || *rptr == '4')
78980482Sdarrenr		ftp->ftp_passok = FTPXY_INIT;
79080482Sdarrenr	else if (ftp->ftp_incok) {
79180482Sdarrenr		if (*rptr == '3') {
79280482Sdarrenr			if (ftp->ftp_passok == FTPXY_ACCT_1)
79380482Sdarrenr				ftp->ftp_passok = FTPXY_GO;
79480482Sdarrenr			else
79580482Sdarrenr				ftp->ftp_passok++;
79680482Sdarrenr		} else if (*rptr == '2') {
79780482Sdarrenr			switch (ftp->ftp_passok)
79880482Sdarrenr			{
79980482Sdarrenr			case FTPXY_USER_1 :
80080482Sdarrenr			case FTPXY_USER_2 :
80180482Sdarrenr			case FTPXY_PASS_1 :
80280482Sdarrenr			case FTPXY_PASS_2 :
80380482Sdarrenr			case FTPXY_ACCT_1 :
80480482Sdarrenr				ftp->ftp_passok = FTPXY_GO;
80580482Sdarrenr				break;
80680482Sdarrenr			default :
80780482Sdarrenr				ftp->ftp_passok += 3;
80880482Sdarrenr				break;
80980482Sdarrenr			}
81080482Sdarrenr		}
81160857Sdarrenr	}
812145522Sdarrenrserver_cmd_ok:
81380482Sdarrenr	ftp->ftp_incok = 0;
814110916Sdarrenr
81560857Sdarrenr	while ((*rptr++ != '\n') && (rptr < wptr))
81660857Sdarrenr		;
81760857Sdarrenr	f->ftps_rptr = rptr;
81860857Sdarrenr	return inc;
81960857Sdarrenr}
82060857Sdarrenr
82160857Sdarrenr
82260857Sdarrenr/*
82360857Sdarrenr * Look to see if the buffer starts with something which we recognise as
82460857Sdarrenr * being the correct syntax for the FTP protocol.
82560857Sdarrenr */
826110916Sdarrenrint ippr_ftp_client_valid(ftps, buf, len)
827110916Sdarrenrftpside_t *ftps;
82860857Sdarrenrchar *buf;
82960857Sdarrenrsize_t len;
83060857Sdarrenr{
831145522Sdarrenr	register char *s, c, pc;
83260857Sdarrenr	register size_t i = len;
833110916Sdarrenr	char cmd[5];
83460857Sdarrenr
835145522Sdarrenr	s = buf;
836145522Sdarrenr
837145522Sdarrenr	if (ftps->ftps_junk == 1)
838145522Sdarrenr		return 1;
839145522Sdarrenr
840110916Sdarrenr	if (i < 5) {
841145522Sdarrenr		if (ippr_ftp_debug > 3)
842145522Sdarrenr			printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i);
84360857Sdarrenr		return 2;
844110916Sdarrenr	}
845145522Sdarrenr
846145522Sdarrenr	i--;
84760857Sdarrenr	c = *s++;
84860857Sdarrenr
849145522Sdarrenr	if (ISALPHA(c)) {
850145522Sdarrenr		cmd[0] = TOUPPER(c);
85160857Sdarrenr		c = *s++;
85260857Sdarrenr		i--;
853145522Sdarrenr		if (ISALPHA(c)) {
854145522Sdarrenr			cmd[1] = TOUPPER(c);
85560857Sdarrenr			c = *s++;
85660857Sdarrenr			i--;
857145522Sdarrenr			if (ISALPHA(c)) {
858145522Sdarrenr				cmd[2] = TOUPPER(c);
85960857Sdarrenr				c = *s++;
86060857Sdarrenr				i--;
861145522Sdarrenr				if (ISALPHA(c)) {
862145522Sdarrenr					cmd[3] = TOUPPER(c);
86392685Sdarrenr					c = *s++;
86492685Sdarrenr					i--;
86592685Sdarrenr					if ((c != ' ') && (c != '\r'))
866110916Sdarrenr						goto bad_client_command;
86792685Sdarrenr				} else if ((c != ' ') && (c != '\r'))
868110916Sdarrenr					goto bad_client_command;
86960857Sdarrenr			} else
870110916Sdarrenr				goto bad_client_command;
87160857Sdarrenr		} else
872110916Sdarrenr			goto bad_client_command;
873110916Sdarrenr	} else {
874110916Sdarrenrbad_client_command:
875145522Sdarrenr		if (ippr_ftp_debug > 3)
876145522Sdarrenr			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
877145522Sdarrenr			       "ippr_ftp_client_valid",
878145522Sdarrenr			       ftps->ftps_junk, (int)len, (int)i, c,
879145522Sdarrenr			       (int)len, (int)len, buf);
88092685Sdarrenr		return 1;
881110916Sdarrenr	}
882110916Sdarrenr
88392685Sdarrenr	for (; i; i--) {
884145522Sdarrenr		pc = c;
88560857Sdarrenr		c = *s++;
886145522Sdarrenr		if ((pc == '\r') && (c == '\n')) {
887110916Sdarrenr			cmd[4] = '\0';
888110916Sdarrenr			if (!strcmp(cmd, "PASV"))
889110916Sdarrenr				ftps->ftps_cmds = FTPXY_C_PASV;
890110916Sdarrenr			else
891110916Sdarrenr				ftps->ftps_cmds = 0;
89292685Sdarrenr			return 0;
893110916Sdarrenr		}
89492685Sdarrenr	}
895145522Sdarrenr#if !defined(_KERNEL)
896145522Sdarrenr	printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n",
897145522Sdarrenr	       (int)len, (int)len, buf);
898110916Sdarrenr#endif
89992685Sdarrenr	return 2;
90092685Sdarrenr}
90192685Sdarrenr
90292685Sdarrenr
903110916Sdarrenrint ippr_ftp_server_valid(ftps, buf, len)
904110916Sdarrenrftpside_t *ftps;
90592685Sdarrenrchar *buf;
90692685Sdarrenrsize_t len;
90792685Sdarrenr{
908145522Sdarrenr	register char *s, c, pc;
90992685Sdarrenr	register size_t i = len;
910110916Sdarrenr	int cmd;
91192685Sdarrenr
912145522Sdarrenr	s = buf;
913145522Sdarrenr	cmd = 0;
914145522Sdarrenr
915145522Sdarrenr	if (ftps->ftps_junk == 1)
916145522Sdarrenr		return 1;
917145522Sdarrenr
918145522Sdarrenr	if (i < 5) {
919145522Sdarrenr		if (ippr_ftp_debug > 3)
920145522Sdarrenr			printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i);
92192685Sdarrenr		return 2;
922145522Sdarrenr	}
923145522Sdarrenr
92492685Sdarrenr	c = *s++;
92592685Sdarrenr	i--;
926145522Sdarrenr	if (c == ' ')
927145522Sdarrenr		goto search_eol;
92892685Sdarrenr
929145522Sdarrenr	if (ISDIGIT(c)) {
930110916Sdarrenr		cmd = (c - '0') * 100;
93192685Sdarrenr		c = *s++;
93260857Sdarrenr		i--;
933145522Sdarrenr		if (ISDIGIT(c)) {
934110916Sdarrenr			cmd += (c - '0') * 10;
93560857Sdarrenr			c = *s++;
93660857Sdarrenr			i--;
937145522Sdarrenr			if (ISDIGIT(c)) {
938110916Sdarrenr				cmd += (c - '0');
93960857Sdarrenr				c = *s++;
94060857Sdarrenr				i--;
94192685Sdarrenr				if ((c != '-') && (c != ' '))
942110916Sdarrenr					goto bad_server_command;
94360857Sdarrenr			} else
944110916Sdarrenr				goto bad_server_command;
94560857Sdarrenr		} else
946110916Sdarrenr			goto bad_server_command;
947110916Sdarrenr	} else {
948110916Sdarrenrbad_server_command:
949145522Sdarrenr		if (ippr_ftp_debug > 3)
950145522Sdarrenr			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
951145522Sdarrenr			       "ippr_ftp_server_valid",
952145522Sdarrenr			       ftps->ftps_junk, (int)len, (int)i,
953145522Sdarrenr			       c, (int)len, (int)len, buf);
95460857Sdarrenr		return 1;
955110916Sdarrenr	}
956145522Sdarrenrsearch_eol:
95760857Sdarrenr	for (; i; i--) {
958145522Sdarrenr		pc = c;
95960857Sdarrenr		c = *s++;
960145522Sdarrenr		if ((pc == '\r') && (c == '\n')) {
961110916Sdarrenr			ftps->ftps_cmds = cmd;
96260857Sdarrenr			return 0;
963110916Sdarrenr		}
96460857Sdarrenr	}
965145522Sdarrenr	if (ippr_ftp_debug > 3)
966145522Sdarrenr		printf("ippr_ftp_server_valid:junk after cmd[%*.*s]\n",
967145522Sdarrenr		       (int)len, (int)len, buf);
96860857Sdarrenr	return 2;
96960857Sdarrenr}
97060857Sdarrenr
97160857Sdarrenr
972110916Sdarrenrint ippr_ftp_valid(ftp, side, buf, len)
973110916Sdarrenrftpinfo_t *ftp;
97492685Sdarrenrint side;
97592685Sdarrenrchar *buf;
97692685Sdarrenrsize_t len;
97792685Sdarrenr{
978110916Sdarrenr	ftpside_t *ftps;
97992685Sdarrenr	int ret;
98092685Sdarrenr
981110916Sdarrenr	ftps = &ftp->ftp_side[side];
982110916Sdarrenr
98392685Sdarrenr	if (side == 0)
984110916Sdarrenr		ret = ippr_ftp_client_valid(ftps, buf, len);
98592685Sdarrenr	else
986110916Sdarrenr		ret = ippr_ftp_server_valid(ftps, buf, len);
98792685Sdarrenr	return ret;
98892685Sdarrenr}
98992685Sdarrenr
99092685Sdarrenr
991102520Sdarrenr/*
992145522Sdarrenr * For map rules, the following applies:
993102520Sdarrenr * rv == 0 for outbound processing,
994102520Sdarrenr * rv == 1 for inbound processing.
995145522Sdarrenr * For rdr rules, the following applies:
996145522Sdarrenr * rv == 0 for inbound processing,
997145522Sdarrenr * rv == 1 for outbound processing.
998102520Sdarrenr */
999145522Sdarrenrint ippr_ftp_process(fin, nat, ftp, rv)
100060857Sdarrenrfr_info_t *fin;
100160857Sdarrenrnat_t *nat;
100260857Sdarrenrftpinfo_t *ftp;
100360857Sdarrenrint rv;
100460857Sdarrenr{
1005102520Sdarrenr	int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff;
1006145522Sdarrenr	char *rptr, *wptr, *s;
1007102520Sdarrenr	u_32_t thseq, thack;
1008102520Sdarrenr	ap_session_t *aps;
100963523Sdarrenr	ftpside_t *f, *t;
101060857Sdarrenr	tcphdr_t *tcp;
1011145522Sdarrenr	ip_t *ip;
101260857Sdarrenr	mb_t *m;
101360857Sdarrenr
1014145522Sdarrenr	m = fin->fin_m;
1015145522Sdarrenr	ip = fin->fin_ip;
101660857Sdarrenr	tcp = (tcphdr_t *)fin->fin_dp;
1017145522Sdarrenr	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
101860857Sdarrenr
1019145522Sdarrenr	f = &ftp->ftp_side[rv];
1020145522Sdarrenr	t = &ftp->ftp_side[1 - rv];
1021145522Sdarrenr	thseq = ntohl(tcp->th_seq);
1022145522Sdarrenr	thack = ntohl(tcp->th_ack);
1023145522Sdarrenr
1024145522Sdarrenr#ifdef __sgi
1025145522Sdarrenr	mlen = fin->fin_plen - off;
102660857Sdarrenr#else
1027145522Sdarrenr	mlen = MSGDSIZE(m) - off;
102860857Sdarrenr#endif
1029145522Sdarrenr	if (ippr_ftp_debug > 4)
1030145522Sdarrenr		printf("ippr_ftp_process: mlen %d\n", mlen);
103172006Sdarrenr
1032145522Sdarrenr	if (mlen <= 0) {
1033145522Sdarrenr		if ((tcp->th_flags & TH_OPENING) == TH_OPENING) {
1034145522Sdarrenr			f->ftps_seq[0] = thseq + 1;
1035145522Sdarrenr			t->ftps_seq[0] = thack;
1036145522Sdarrenr		}
1037145522Sdarrenr		return 0;
1038145522Sdarrenr	}
1039102520Sdarrenr	aps = nat->nat_aps;
104060857Sdarrenr
1041102520Sdarrenr	sel = aps->aps_sel[1 - rv];
1042102520Sdarrenr	sel2 = aps->aps_sel[rv];
1043102520Sdarrenr	if (rv == 0) {
1044102520Sdarrenr		seqoff = aps->aps_seqoff[sel];
1045102520Sdarrenr		if (aps->aps_seqmin[sel] > seqoff + thseq)
1046102520Sdarrenr			seqoff = aps->aps_seqoff[!sel];
1047102520Sdarrenr		ackoff = aps->aps_ackoff[sel2];
1048102520Sdarrenr		if (aps->aps_ackmin[sel2] > ackoff + thack)
1049102520Sdarrenr			ackoff = aps->aps_ackoff[!sel2];
1050102520Sdarrenr	} else {
1051102520Sdarrenr		seqoff = aps->aps_ackoff[sel];
1052145522Sdarrenr		if (ippr_ftp_debug > 2)
1053145522Sdarrenr			printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1054145522Sdarrenr			       aps->aps_ackmin[sel]);
1055102520Sdarrenr		if (aps->aps_ackmin[sel] > seqoff + thseq)
1056102520Sdarrenr			seqoff = aps->aps_ackoff[!sel];
105760857Sdarrenr
1058102520Sdarrenr		ackoff = aps->aps_seqoff[sel2];
1059145522Sdarrenr		if (ippr_ftp_debug > 2)
1060145522Sdarrenr			printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1061145522Sdarrenr			       aps->aps_seqmin[sel2]);
1062102520Sdarrenr		if (ackoff > 0) {
1063102520Sdarrenr			if (aps->aps_seqmin[sel2] > ackoff + thack)
1064102520Sdarrenr				ackoff = aps->aps_seqoff[!sel2];
1065102520Sdarrenr		} else {
1066102520Sdarrenr			if (aps->aps_seqmin[sel2] > thack)
1067102520Sdarrenr				ackoff = aps->aps_seqoff[!sel2];
1068102520Sdarrenr		}
106995563Sdarrenr	}
1070145522Sdarrenr	if (ippr_ftp_debug > 2) {
1071145522Sdarrenr		printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1072145522Sdarrenr		       rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1073145522Sdarrenr		       thack, ackoff, mlen, fin->fin_plen, off);
1074145522Sdarrenr		printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1075145522Sdarrenr		       aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1076145522Sdarrenr		       aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1077145522Sdarrenr		printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1078145522Sdarrenr		       aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1079145522Sdarrenr		       aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1080145522Sdarrenr	}
1081102520Sdarrenr
108260857Sdarrenr	/*
108360857Sdarrenr	 * XXX - Ideally, this packet should get dropped because we now know
108460857Sdarrenr	 * that it is out of order (and there is no real danger in doing so
108560857Sdarrenr	 * apart from causing packets to go through here ordered).
108660857Sdarrenr	 */
1087145522Sdarrenr	if (ippr_ftp_debug > 2) {
1088145522Sdarrenr		printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1089145522Sdarrenr		       rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1090145522Sdarrenr	}
1091102520Sdarrenr
1092102520Sdarrenr	ok = 0;
1093110916Sdarrenr	if (t->ftps_seq[0] == 0) {
1094110916Sdarrenr		t->ftps_seq[0] = thack;
1095110916Sdarrenr		ok = 1;
1096110916Sdarrenr	} else {
1097102520Sdarrenr		if (ackoff == 0) {
1098102520Sdarrenr			if (t->ftps_seq[0] == thack)
1099102520Sdarrenr				ok = 1;
1100102520Sdarrenr			else if (t->ftps_seq[1] == thack) {
1101102520Sdarrenr				t->ftps_seq[0] = thack;
1102102520Sdarrenr				ok = 1;
1103102520Sdarrenr			}
1104102520Sdarrenr		} else {
1105102520Sdarrenr			if (t->ftps_seq[0] + ackoff == thack)
1106102520Sdarrenr				ok = 1;
1107102520Sdarrenr			else if (t->ftps_seq[0] == thack + ackoff)
1108102520Sdarrenr				ok = 1;
1109102520Sdarrenr			else if (t->ftps_seq[1] + ackoff == thack) {
1110102520Sdarrenr				t->ftps_seq[0] = thack - ackoff;
1111102520Sdarrenr				ok = 1;
1112102520Sdarrenr			} else if (t->ftps_seq[1] == thack + ackoff) {
1113102520Sdarrenr				t->ftps_seq[0] = thack - ackoff;
1114102520Sdarrenr				ok = 1;
1115102520Sdarrenr			}
1116102520Sdarrenr		}
1117102520Sdarrenr	}
1118102520Sdarrenr
1119145522Sdarrenr	if (ippr_ftp_debug > 2) {
1120145522Sdarrenr		if (!ok)
1121145522Sdarrenr			printf("%s ok\n", "not");
1122145522Sdarrenr	}
1123102520Sdarrenr
1124102520Sdarrenr	if (!mlen) {
1125110916Sdarrenr		if (t->ftps_seq[0] + ackoff != thack) {
1126145522Sdarrenr			if (ippr_ftp_debug > 1) {
1127145522Sdarrenr				printf("%s:seq[0](%x) + (%x) != (%x)\n",
1128145522Sdarrenr				       "ippr_ftp_process", t->ftps_seq[0],
1129145522Sdarrenr				       ackoff, thack);
1130145522Sdarrenr			}
113195563Sdarrenr			return APR_ERR(1);
1132110916Sdarrenr		}
1133102520Sdarrenr
1134145522Sdarrenr		if (ippr_ftp_debug > 2) {
1135145522Sdarrenr			printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n",
1136145522Sdarrenr				f->ftps_seq[0], f->ftps_seq[1]);
1137145522Sdarrenr		}
1138145522Sdarrenr
1139102520Sdarrenr		if (tcp->th_flags & TH_FIN) {
1140110916Sdarrenr			if (thseq == f->ftps_seq[1]) {
1141110916Sdarrenr				f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1142110916Sdarrenr				f->ftps_seq[1] = thseq + 1 - seqoff;
1143110916Sdarrenr			} else {
1144145522Sdarrenr				if (ippr_ftp_debug > 1) {
1145145522Sdarrenr					printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1146145522Sdarrenr					       thseq, seqoff, f->ftps_seq[0]);
1147145522Sdarrenr				}
1148102520Sdarrenr				return APR_ERR(1);
1149102520Sdarrenr			}
115095563Sdarrenr		}
1151102520Sdarrenr		f->ftps_len = 0;
1152102520Sdarrenr		return 0;
115360857Sdarrenr	}
1154102520Sdarrenr
1155102520Sdarrenr	ok = 0;
1156110916Sdarrenr	if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1157102520Sdarrenr		ok = 1;
1158102520Sdarrenr	/*
1159102520Sdarrenr	 * Retransmitted data packet.
1160102520Sdarrenr	 */
1161110916Sdarrenr	} else if ((thseq + mlen == f->ftps_seq[0]) ||
1162110916Sdarrenr		   (thseq + mlen == f->ftps_seq[1])) {
1163102520Sdarrenr		ok = 1;
1164110916Sdarrenr	}
1165110916Sdarrenr
1166102520Sdarrenr	if (ok == 0) {
1167102520Sdarrenr		inc = thseq - f->ftps_seq[0];
1168145522Sdarrenr		if (ippr_ftp_debug > 1) {
1169145522Sdarrenr			printf("inc %d sel %d rv %d\n", inc, sel, rv);
1170145522Sdarrenr			printf("th_seq %x ftps_seq %x/%x\n",
1171145522Sdarrenr			       thseq, f->ftps_seq[0], f->ftps_seq[1]);
1172145522Sdarrenr			printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1173145522Sdarrenr			       aps->aps_ackoff[sel]);
1174145522Sdarrenr			printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1175145522Sdarrenr			       aps->aps_seqoff[sel]);
1176145522Sdarrenr		}
1177102520Sdarrenr
1178102520Sdarrenr		return APR_ERR(1);
1179102520Sdarrenr	}
1180102520Sdarrenr
118195563Sdarrenr	inc = 0;
1182102520Sdarrenr	rptr = f->ftps_rptr;
1183102520Sdarrenr	wptr = f->ftps_wptr;
1184102520Sdarrenr	f->ftps_seq[0] = thseq;
1185102520Sdarrenr	f->ftps_seq[1] = f->ftps_seq[0] + mlen;
118672006Sdarrenr	f->ftps_len = mlen;
118760857Sdarrenr
118860857Sdarrenr	while (mlen > 0) {
1189145522Sdarrenr		len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1190145522Sdarrenr		COPYDATA(m, off, len, wptr);
119160857Sdarrenr		mlen -= len;
119260857Sdarrenr		off += len;
119360857Sdarrenr		wptr += len;
1194145522Sdarrenr
1195145522Sdarrenr		if (ippr_ftp_debug > 3)
1196145522Sdarrenr			printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
1197145522Sdarrenr			       "ippr_ftp_process",
1198145522Sdarrenr			       len, mlen, off, (u_long)wptr, f->ftps_junk,
1199145522Sdarrenr			       len, len, rptr);
1200145522Sdarrenr
120160857Sdarrenr		f->ftps_wptr = wptr;
1202145522Sdarrenr		if (f->ftps_junk != 0) {
1203145522Sdarrenr			i = f->ftps_junk;
1204110916Sdarrenr			f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1205110916Sdarrenr						      wptr - rptr);
120660857Sdarrenr
1207145522Sdarrenr			if (ippr_ftp_debug > 5)
1208145522Sdarrenr				printf("%s:junk %d -> %d\n",
1209145522Sdarrenr				       "ippr_ftp_process", i, f->ftps_junk);
1210145522Sdarrenr
1211145522Sdarrenr			if (f->ftps_junk != 0) {
1212145522Sdarrenr				if (wptr - rptr == sizeof(f->ftps_buf)) {
1213145522Sdarrenr					if (ippr_ftp_debug > 4)
1214145522Sdarrenr						printf("%s:full buffer\n",
1215145522Sdarrenr						       "ippr_ftp_process");
1216145522Sdarrenr					f->ftps_rptr = f->ftps_buf;
1217145522Sdarrenr					f->ftps_wptr = f->ftps_buf;
1218145522Sdarrenr					rptr = f->ftps_rptr;
1219145522Sdarrenr					wptr = f->ftps_wptr;
1220145522Sdarrenr					/*
1221145522Sdarrenr					 * Because we throw away data here that
1222145522Sdarrenr					 * we would otherwise parse, set the
1223145522Sdarrenr					 * junk flag to indicate just ignore
1224145522Sdarrenr					 * any data upto the next CRLF.
1225145522Sdarrenr					 */
1226145522Sdarrenr					f->ftps_junk = 1;
1227145522Sdarrenr					continue;
1228145522Sdarrenr				}
1229145522Sdarrenr			}
1230145522Sdarrenr		}
1231145522Sdarrenr
123260857Sdarrenr		while ((f->ftps_junk == 0) && (wptr > rptr)) {
1233145522Sdarrenr			len = wptr - rptr;
1234145522Sdarrenr			f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len);
1235145522Sdarrenr
1236145522Sdarrenr			if (ippr_ftp_debug > 3) {
1237145522Sdarrenr				printf("%s=%d len %d rv %d ptr %lx/%lx ",
1238145522Sdarrenr				       "ippr_ftp_valid",
1239145522Sdarrenr				       f->ftps_junk, len, rv, (u_long)rptr,
1240145522Sdarrenr				       (u_long)wptr);
1241145522Sdarrenr				printf("buf [%*.*s]\n", len, len, rptr);
1242145522Sdarrenr			}
1243145522Sdarrenr
124460857Sdarrenr			if (f->ftps_junk == 0) {
124560857Sdarrenr				f->ftps_rptr = rptr;
124660857Sdarrenr				if (rv)
124760857Sdarrenr					inc += ippr_ftp_server(fin, ip, nat,
124860857Sdarrenr							       ftp, len);
124960857Sdarrenr				else
125060857Sdarrenr					inc += ippr_ftp_client(fin, ip, nat,
125160857Sdarrenr							       ftp, len);
125260857Sdarrenr				rptr = f->ftps_rptr;
125392685Sdarrenr				wptr = f->ftps_wptr;
125460857Sdarrenr			}
125560857Sdarrenr		}
125660857Sdarrenr
125792685Sdarrenr		/*
125892685Sdarrenr		 * Off to a bad start so lets just forget about using the
125992685Sdarrenr		 * ftp proxy for this connection.
126092685Sdarrenr		 */
126195563Sdarrenr		if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) {
1262102520Sdarrenr			/* f->ftps_seq[1] += inc; */
1263145522Sdarrenr
1264145522Sdarrenr			if (ippr_ftp_debug > 1)
1265145522Sdarrenr				printf("%s:cmds == 0 junk == 1\n",
1266145522Sdarrenr				       "ippr_ftp_process");
126792685Sdarrenr			return APR_ERR(2);
126895563Sdarrenr		}
126992685Sdarrenr
1270145522Sdarrenr		if ((f->ftps_junk != 0) && (rptr < wptr)) {
1271145522Sdarrenr			for (s = rptr; s < wptr; s++) {
1272145522Sdarrenr				if ((*s == '\r') && (s + 1 < wptr) &&
1273145522Sdarrenr				    (*(s + 1) == '\n')) {
1274145522Sdarrenr					rptr = s + 2;
1275145522Sdarrenr					f->ftps_junk = 0;
127667614Sdarrenr					break;
1277145522Sdarrenr				}
127860857Sdarrenr			}
127960857Sdarrenr		}
128060857Sdarrenr
128160857Sdarrenr		if (rptr == wptr) {
128260857Sdarrenr			rptr = wptr = f->ftps_buf;
128360857Sdarrenr		} else {
1284145522Sdarrenr			/*
1285145522Sdarrenr			 * Compact the buffer back to the start.  The junk
1286145522Sdarrenr			 * flag should already be set and because we're not
1287145522Sdarrenr			 * throwing away any data, it is preserved from its
1288145522Sdarrenr			 * current state.
1289145522Sdarrenr			 */
1290145522Sdarrenr			if (rptr > f->ftps_buf) {
1291145522Sdarrenr				bcopy(rptr, f->ftps_buf, len);
1292145522Sdarrenr				wptr -= rptr - f->ftps_buf;
1293145522Sdarrenr				rptr = f->ftps_buf;
129460857Sdarrenr			}
129560857Sdarrenr		}
1296145522Sdarrenr		f->ftps_rptr = rptr;
1297145522Sdarrenr		f->ftps_wptr = wptr;
129860857Sdarrenr	}
129960857Sdarrenr
1300102520Sdarrenr	/* f->ftps_seq[1] += inc; */
1301102520Sdarrenr	if (tcp->th_flags & TH_FIN)
1302102520Sdarrenr		f->ftps_seq[1]++;
1303145522Sdarrenr	if (ippr_ftp_debug > 3) {
1304145522Sdarrenr#ifdef __sgi
1305145522Sdarrenr		mlen = fin->fin_plen;
1306145522Sdarrenr#else
1307145522Sdarrenr		mlen = MSGDSIZE(m);
1308102520Sdarrenr#endif
1309145522Sdarrenr		mlen -= off;
1310145522Sdarrenr		printf("ftps_seq[1] = %x inc %d len %d\n",
1311145522Sdarrenr		       f->ftps_seq[1], inc, mlen);
1312145522Sdarrenr	}
1313102520Sdarrenr
131460857Sdarrenr	f->ftps_rptr = rptr;
131560857Sdarrenr	f->ftps_wptr = wptr;
131664580Sdarrenr	return APR_INC(inc);
131760857Sdarrenr}
131860857Sdarrenr
131960857Sdarrenr
1320145522Sdarrenrint ippr_ftp_out(fin, aps, nat)
132160857Sdarrenrfr_info_t *fin;
132260857Sdarrenrap_session_t *aps;
132360857Sdarrenrnat_t *nat;
132460857Sdarrenr{
132560857Sdarrenr	ftpinfo_t *ftp;
1326145522Sdarrenr	int rev;
132760857Sdarrenr
132860857Sdarrenr	ftp = aps->aps_data;
132960857Sdarrenr	if (ftp == NULL)
133060857Sdarrenr		return 0;
1331145522Sdarrenr
1332145522Sdarrenr	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1333145522Sdarrenr	if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1334145522Sdarrenr		ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1335145522Sdarrenr
1336145522Sdarrenr	return ippr_ftp_process(fin, nat, ftp, rev);
133760857Sdarrenr}
133860857Sdarrenr
133960857Sdarrenr
1340145522Sdarrenrint ippr_ftp_in(fin, aps, nat)
134153642Sguidofr_info_t *fin;
134253642Sguidoap_session_t *aps;
134353642Sguidonat_t *nat;
134453642Sguido{
134560857Sdarrenr	ftpinfo_t *ftp;
1346145522Sdarrenr	int rev;
134753642Sguido
134860857Sdarrenr	ftp = aps->aps_data;
134960857Sdarrenr	if (ftp == NULL)
135060857Sdarrenr		return 0;
1351145522Sdarrenr
1352145522Sdarrenr	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1353145522Sdarrenr	if (ftp->ftp_side[rev].ftps_ifp == NULL)
1354145522Sdarrenr		ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1355145522Sdarrenr
1356145522Sdarrenr	return ippr_ftp_process(fin, nat, ftp, 1 - rev);
135753642Sguido}
135860857Sdarrenr
135960857Sdarrenr
136060857Sdarrenr/*
136160857Sdarrenr * ippr_ftp_atoi - implement a version of atoi which processes numbers in
136260857Sdarrenr * pairs separated by commas (which are expected to be in the range 0 - 255),
136360857Sdarrenr * returning a 16 bit number combining either side of the , as the MSB and
136460857Sdarrenr * LSB.
136560857Sdarrenr */
136660857Sdarrenru_short ippr_ftp_atoi(ptr)
136760857Sdarrenrchar **ptr;
136860857Sdarrenr{
136960857Sdarrenr	register char *s = *ptr, c;
137060857Sdarrenr	register u_char i = 0, j = 0;
137160857Sdarrenr
1372145522Sdarrenr	while (((c = *s++) != '\0') && ISDIGIT(c)) {
137360857Sdarrenr		i *= 10;
137460857Sdarrenr		i += c - '0';
137560857Sdarrenr	}
137660857Sdarrenr	if (c != ',') {
137760857Sdarrenr		*ptr = NULL;
137860857Sdarrenr		return 0;
137960857Sdarrenr	}
1380145522Sdarrenr	while (((c = *s++) != '\0') && ISDIGIT(c)) {
138160857Sdarrenr		j *= 10;
138260857Sdarrenr		j += c - '0';
138360857Sdarrenr	}
138460857Sdarrenr	*ptr = s;
138567614Sdarrenr	i &= 0xff;
138667614Sdarrenr	j &= 0xff;
138760857Sdarrenr	return (i << 8) | j;
138860857Sdarrenr}
1389145522Sdarrenr
1390145522Sdarrenr
1391145522Sdarrenrint ippr_ftp_epsv(fin, ip, nat, f, dlen)
1392145522Sdarrenrfr_info_t *fin;
1393145522Sdarrenrip_t *ip;
1394145522Sdarrenrnat_t *nat;
1395145522Sdarrenrftpside_t *f;
1396145522Sdarrenrint dlen;
1397145522Sdarrenr{
1398145522Sdarrenr	char newbuf[IPF_FTPBUFSZ];
1399145522Sdarrenr	char *s;
1400145522Sdarrenr	u_short ap = 0;
1401145522Sdarrenr
1402145522Sdarrenr#define EPSV_REPLEN	33
1403145522Sdarrenr	/*
1404145522Sdarrenr	 * Check for EPSV reply message.
1405145522Sdarrenr	 */
1406145522Sdarrenr	if (dlen < IPF_MIN229LEN)
1407145522Sdarrenr		return (0);
1408145522Sdarrenr	else if (strncmp(f->ftps_rptr,
1409145522Sdarrenr			 "229 Entering Extended Passive Mode", EPSV_REPLEN))
1410145522Sdarrenr		return (0);
1411145522Sdarrenr
1412145522Sdarrenr	/*
1413145522Sdarrenr	 * Skip the EPSV command + space
1414145522Sdarrenr	 */
1415145522Sdarrenr	s = f->ftps_rptr + 33;
1416145522Sdarrenr	while (*s && !ISDIGIT(*s))
1417145522Sdarrenr		s++;
1418145522Sdarrenr
1419145522Sdarrenr	/*
1420145522Sdarrenr	 * As per RFC 2428, there are no addres components in the EPSV
1421145522Sdarrenr	 * response.  So we'll go straight to getting the port.
1422145522Sdarrenr	 */
1423145522Sdarrenr	while (*s && ISDIGIT(*s)) {
1424145522Sdarrenr		ap *= 10;
1425145522Sdarrenr		ap += *s++ - '0';
1426145522Sdarrenr	}
1427145522Sdarrenr
1428145522Sdarrenr	if (!s)
1429145522Sdarrenr		return 0;
1430145522Sdarrenr
1431145522Sdarrenr	if (*s == '|')
1432145522Sdarrenr		s++;
1433145522Sdarrenr	if (*s == ')')
1434145522Sdarrenr		s++;
1435145522Sdarrenr	if (*s == '\n')
1436145522Sdarrenr		s--;
1437145522Sdarrenr	/*
1438145522Sdarrenr	 * check for CR-LF at the end.
1439145522Sdarrenr	 */
1440145522Sdarrenr	if ((*s == '\r') && (*(s + 1) == '\n')) {
1441145522Sdarrenr		s += 2;
1442145522Sdarrenr	} else
1443145522Sdarrenr		return 0;
1444145522Sdarrenr
1445145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL)
1446145522Sdarrenr	SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1447145522Sdarrenr		 "229 Entering Extended Passive Mode", ap);
1448145522Sdarrenr#else
1449145522Sdarrenr	(void) sprintf(newbuf, "%s (|||%u|)\r\n",
1450145522Sdarrenr		       "229 Entering Extended Passive Mode", ap);
1451145522Sdarrenr#endif
1452145522Sdarrenr
1453145522Sdarrenr	return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s,
1454145522Sdarrenr				  ip->ip_src.s_addr);
1455145522Sdarrenr}
1456