1145522Sdarrenr/*	$FreeBSD: stable/10/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 328203 2018-01-20 18:00:42Z cy $	*/
2145522Sdarrenr
353642Sguido/*
4255332Scy * Copyright (C) 2012 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: stable/10/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 328203 2018-01-20 18:00:42Z cy $
12161356Sguido * Id: ip_ftp_pxy.c,v 2.88.2.19 2006/04/01 10:14:53 darrenr Exp $
1353642Sguido */
1453642Sguido
1553642Sguido#define	IPF_FTP_PROXY
1653642Sguido
1753642Sguido#define	IPF_MINPORTLEN	18
18255332Scy#define	IPF_MINEPRTLEN	20
1953642Sguido#define	IPF_MAXPORTLEN	30
2053642Sguido#define	IPF_MIN227LEN	39
2153642Sguido#define	IPF_MAX227LEN	51
22145522Sdarrenr#define	IPF_MIN229LEN	47
23145522Sdarrenr#define	IPF_MAX229LEN	51
2453642Sguido
2580482Sdarrenr#define	FTPXY_GO	0
2680482Sdarrenr#define	FTPXY_INIT	1
2780482Sdarrenr#define	FTPXY_USER_1	2
2880482Sdarrenr#define	FTPXY_USOK_1	3
2980482Sdarrenr#define	FTPXY_PASS_1	4
3080482Sdarrenr#define	FTPXY_PAOK_1	5
3180482Sdarrenr#define	FTPXY_AUTH_1	6
3280482Sdarrenr#define	FTPXY_AUOK_1	7
3380482Sdarrenr#define	FTPXY_ADAT_1	8
3480482Sdarrenr#define	FTPXY_ADOK_1	9
3580482Sdarrenr#define	FTPXY_ACCT_1	10
3680482Sdarrenr#define	FTPXY_ACOK_1	11
3780482Sdarrenr#define	FTPXY_USER_2	12
3880482Sdarrenr#define	FTPXY_USOK_2	13
3980482Sdarrenr#define	FTPXY_PASS_2	14
4080482Sdarrenr#define	FTPXY_PAOK_2	15
4153642Sguido
42255332Scy#define	FTPXY_JUNK_OK	0
43255332Scy#define	FTPXY_JUNK_BAD	1	/* Ignore all commands for this connection */
44255332Scy#define	FTPXY_JUNK_EOL	2	/* consume the rest of this line only */
45255332Scy#define	FTPXY_JUNK_CONT	3	/* Saerching for next numeric */
46255332Scy
47110916Sdarrenr/*
48110916Sdarrenr * Values for FTP commands.  Numerics cover 0-999
49110916Sdarrenr */
50110916Sdarrenr#define	FTPXY_C_PASV	1000
51255332Scy#define	FTPXY_C_PORT	1001
52255332Scy#define	FTPXY_C_EPSV	1002
53255332Scy#define	FTPXY_C_EPRT	1003
54110916Sdarrenr
5553642Sguido
56255332Scytypedef struct ipf_ftp_softc_s {
57255332Scy	int	ipf_p_ftp_pasvonly;
58255332Scy	/* Do not require logins before transfers */
59255332Scy	int	ipf_p_ftp_insecure;
60255332Scy	int	ipf_p_ftp_pasvrdr;
61255332Scy	/* PASV must be last command prior to 227 */
62255332Scy	int	ipf_p_ftp_forcepasv;
63255332Scy	int	ipf_p_ftp_debug;
64255332Scy	int	ipf_p_ftp_single_xfer;
65255332Scy	void	*ipf_p_ftp_tune;
66255332Scy} ipf_ftp_softc_t;
67145522Sdarrenr
68255332Scy
69255332Scyvoid ipf_p_ftp_main_load __P((void));
70255332Scyvoid ipf_p_ftp_main_unload __P((void));
71255332Scyvoid *ipf_p_ftp_soft_create __P((ipf_main_softc_t *));
72255332Scyvoid ipf_p_ftp_soft_destroy __P((ipf_main_softc_t *, void *));
73255332Scy
74255332Scyint ipf_p_ftp_client __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
75255332Scy			  ftpinfo_t *, int));
76255332Scyint ipf_p_ftp_complete __P((char *, size_t));
77255332Scyint ipf_p_ftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *));
78255332Scyint ipf_p_ftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *));
79255332Scyvoid ipf_p_ftp_del __P((ipf_main_softc_t *, ap_session_t *));
80255332Scyint ipf_p_ftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *));
81255332Scyint ipf_p_ftp_pasv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
82255332Scy			ftpinfo_t *, int));
83255332Scyint ipf_p_ftp_epsv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
84255332Scy			ftpinfo_t *, int));
85255332Scyint ipf_p_ftp_port __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
86255332Scy			ftpinfo_t *, int));
87255332Scyint ipf_p_ftp_process __P((ipf_ftp_softc_t *, fr_info_t *, nat_t *,
88255332Scy			   ftpinfo_t *, int));
89255332Scyint ipf_p_ftp_server __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
90255332Scy			  ftpinfo_t *, int));
91255332Scyint ipf_p_ftp_valid __P((ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t));
92255332Scyint ipf_p_ftp_server_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *,
93255332Scy				size_t));
94255332Scyint ipf_p_ftp_client_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *,
95255332Scy				size_t));
96255332Scyu_short ipf_p_ftp_atoi __P((char **));
97255332Scyint ipf_p_ftp_pasvreply __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
98255332Scy			     ftpinfo_t *, u_int, char *, char *));
99255332Scyint ipf_p_ftp_eprt __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
100255332Scy			ftpinfo_t *, int));
101255332Scyint ipf_p_ftp_eprt4 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
102255332Scy			 ftpinfo_t *, int));
103255332Scyint ipf_p_ftp_eprt6 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
104255332Scy			 ftpinfo_t *, int));
105255332Scyint ipf_p_ftp_addport __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
106255332Scy			   ftpinfo_t *, int, int, int));
107255332Scyvoid ipf_p_ftp_setpending __P((ipf_main_softc_t *, ftpinfo_t *));
108255332Scy
109145522Sdarrenr/*
110255332Scy * Debug levels
111145522Sdarrenr */
112255332Scy#define	DEBUG_SECURITY		0x01
113255332Scy#define	DEBUG_ERROR		0x02
114255332Scy#define	DEBUG_INFO		0x04
115255332Scy#define	DEBUG_PARSE_ERR		0x08
116255332Scy#define	DEBUG_PARSE_INFO	0x10
117255332Scy#define	DEBUG_PARSE		0x20
11853642Sguido
119255332Scystatic	int	ipf_p_ftp_proxy_init = 0;
120145522Sdarrenrstatic	frentry_t	ftppxyfr;
121255332Scystatic	ipftuneable_t	ipf_ftp_tuneables[] = {
122255332Scy	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) },
123255332Scy		"ftp_debug",	0,	0x7f,
124255332Scy		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug),
125255332Scy		0, NULL, NULL },
126255332Scy	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) },
127255332Scy		"ftp_pasvonly",	0,	1,
128255332Scy		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly),
129255332Scy		0, NULL, NULL },
130255332Scy	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) },
131255332Scy		"ftp_insecure",	0,	1,
132255332Scy		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure),
133255332Scy		0, NULL, NULL },
134255332Scy	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) },
135255332Scy		"ftp_pasvrdr",	0,	1,
136255332Scy		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr),
137255332Scy		0, NULL, NULL },
138255332Scy	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) },
139255332Scy		"ftp_forcepasv", 0,	1,
140255332Scy		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv),
141255332Scy		0, NULL, NULL },
142255332Scy	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) },
143255332Scy		"ftp_single_xfer", 0,	1,
144255332Scy		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer),
145255332Scy		0, NULL, NULL },
146255332Scy	{ { NULL }, NULL, 0, 0, 0, 0, NULL, NULL }
147145522Sdarrenr};
14853642Sguido
149145522Sdarrenr
150255332Scyvoid
151255332Scyipf_p_ftp_main_load()
15253642Sguido{
15392685Sdarrenr	bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
15492685Sdarrenr	ftppxyfr.fr_ref = 1;
15592685Sdarrenr	ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
156255332Scy
157145522Sdarrenr	MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex");
158255332Scy	ipf_p_ftp_proxy_init = 1;
15953642Sguido}
16053642Sguido
16153642Sguido
162255332Scyvoid
163255332Scyipf_p_ftp_main_unload()
164145522Sdarrenr{
165145522Sdarrenr
166255332Scy	if (ipf_p_ftp_proxy_init == 1) {
167145522Sdarrenr		MUTEX_DESTROY(&ftppxyfr.fr_lock);
168255332Scy		ipf_p_ftp_proxy_init = 0;
169145522Sdarrenr	}
170145522Sdarrenr}
171145522Sdarrenr
172145522Sdarrenr
173255332Scy/*
174255332Scy * Initialize local structures.
175255332Scy */
176255332Scyvoid *
177255332Scyipf_p_ftp_soft_create(softc)
178255332Scy	ipf_main_softc_t *softc;
17953642Sguido{
180255332Scy	ipf_ftp_softc_t *softf;
181255332Scy
182255332Scy	KMALLOC(softf, ipf_ftp_softc_t *);
183255332Scy	if (softf == NULL)
184255332Scy		return NULL;
185255332Scy
186255332Scy	bzero((char *)softf, sizeof(*softf));
187255332Scy#if defined(_KERNEL)
188255332Scy	softf->ipf_p_ftp_debug = 0;
189255332Scy#else
190255332Scy	softf->ipf_p_ftp_debug = DEBUG_PARSE_ERR;
191255332Scy#endif
192255332Scy	softf->ipf_p_ftp_forcepasv = 1;
193255332Scy
194255332Scy	softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf,
195255332Scy						    sizeof(ipf_ftp_tuneables),
196255332Scy						    ipf_ftp_tuneables);
197255332Scy	if (softf->ipf_p_ftp_tune == NULL) {
198255332Scy		ipf_p_ftp_soft_destroy(softc, softf);
199255332Scy		return NULL;
200255332Scy	}
201255332Scy	if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) {
202255332Scy		ipf_p_ftp_soft_destroy(softc, softf);
203255332Scy		return NULL;
204255332Scy	}
205255332Scy
206255332Scy	return softf;
207255332Scy}
208255332Scy
209255332Scy
210255332Scyvoid
211255332Scyipf_p_ftp_soft_destroy(softc, arg)
212255332Scy	ipf_main_softc_t *softc;
213255332Scy	void *arg;
214255332Scy{
215255332Scy	ipf_ftp_softc_t *softf = arg;
216255332Scy
217255332Scy	if (softf->ipf_p_ftp_tune != NULL) {
218255332Scy		ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune);
219255332Scy		KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables));
220255332Scy		softf->ipf_p_ftp_tune = NULL;
221255332Scy	}
222255332Scy
223255332Scy	KFREE(softf);
224255332Scy}
225255332Scy
226255332Scy
227255332Scyint
228255332Scyipf_p_ftp_new(arg, fin, aps, nat)
229255332Scy	void *arg;
230255332Scy	fr_info_t *fin;
231255332Scy	ap_session_t *aps;
232255332Scy	nat_t *nat;
233255332Scy{
23460857Sdarrenr	ftpinfo_t *ftp;
23560857Sdarrenr	ftpside_t *f;
23653642Sguido
23760857Sdarrenr	KMALLOC(ftp, ftpinfo_t *);
23860857Sdarrenr	if (ftp == NULL)
23960857Sdarrenr		return -1;
240145522Sdarrenr
241145522Sdarrenr	nat = nat;	/* LINT */
242145522Sdarrenr
24360857Sdarrenr	aps->aps_data = ftp;
24460857Sdarrenr	aps->aps_psiz = sizeof(ftpinfo_t);
245255332Scy	aps->aps_sport = htons(fin->fin_sport);
246255332Scy	aps->aps_dport = htons(fin->fin_dport);
24760857Sdarrenr
24860857Sdarrenr	bzero((char *)ftp, sizeof(*ftp));
24960857Sdarrenr	f = &ftp->ftp_side[0];
25060857Sdarrenr	f->ftps_rptr = f->ftps_buf;
25160857Sdarrenr	f->ftps_wptr = f->ftps_buf;
25260857Sdarrenr	f = &ftp->ftp_side[1];
25360857Sdarrenr	f->ftps_rptr = f->ftps_buf;
25460857Sdarrenr	f->ftps_wptr = f->ftps_buf;
25580482Sdarrenr	ftp->ftp_passok = FTPXY_INIT;
256145522Sdarrenr	ftp->ftp_incok = 0;
25760857Sdarrenr	return 0;
25853642Sguido}
25953642Sguido
26053642Sguido
261255332Scyvoid
262255332Scyipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp)
26353642Sguido{
264255332Scy	if (ftp->ftp_pendnat != NULL)
265255332Scy		ipf_nat_setpending(softc, ftp->ftp_pendnat);
266255332Scy
267255332Scy	if (ftp->ftp_pendstate != NULL) {
268255332Scy		READ_ENTER(&softc->ipf_state);
269255332Scy		ipf_state_setpending(softc, ftp->ftp_pendstate);
270255332Scy		RWLOCK_EXIT(&softc->ipf_state);
271255332Scy	}
272255332Scy}
273255332Scy
274255332Scy
275255332Scyvoid
276255332Scyipf_p_ftp_del(softc, aps)
277255332Scy	ipf_main_softc_t *softc;
278255332Scy	ap_session_t *aps;
279255332Scy{
280255332Scy	ftpinfo_t *ftp;
281255332Scy
282255332Scy	ftp = aps->aps_data;
283255332Scy	if (ftp != NULL)
284255332Scy		ipf_p_ftp_setpending(softc, ftp);
285255332Scy}
286255332Scy
287255332Scy
288255332Scyint
289255332Scyipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen)
290255332Scy	ipf_ftp_softc_t *softf;
291255332Scy	fr_info_t *fin;
292255332Scy	ip_t *ip;
293255332Scy	nat_t *nat;
294255332Scy	ftpinfo_t *ftp;
295255332Scy	int dlen;
296255332Scy{
29760857Sdarrenr	char newbuf[IPF_FTPBUFSZ], *s;
29853642Sguido	u_int a1, a2, a3, a4;
29992685Sdarrenr	u_short a5, a6, sp;
30060857Sdarrenr	size_t nlen, olen;
301255332Scy	tcphdr_t *tcp;
302255332Scy	int inc, off;
303255332Scy	ftpside_t *f;
30453642Sguido	mb_t *m;
30553642Sguido
306145522Sdarrenr	m = fin->fin_m;
307255332Scy	f = &ftp->ftp_side[0];
30853642Sguido	tcp = (tcphdr_t *)fin->fin_dp;
309145522Sdarrenr	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
310145522Sdarrenr
31160857Sdarrenr	/*
31260857Sdarrenr	 * Check for client sending out PORT message.
31360857Sdarrenr	 */
314110916Sdarrenr	if (dlen < IPF_MINPORTLEN) {
315255332Scy		DT3(ftp_PORT_error_dlen, nat_t *, nat, ftpside_t *, f,
316255332Scy		    u_int, dlen);
317255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
318255332Scy			printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
319145522Sdarrenr			       dlen);
32053642Sguido		return 0;
321110916Sdarrenr	}
32260857Sdarrenr	/*
32353642Sguido	 * Skip the PORT command + space
32453642Sguido	 */
32560857Sdarrenr	s = f->ftps_rptr + 5;
32653642Sguido	/*
32753642Sguido	 * Pick out the address components, two at a time.
32853642Sguido	 */
329255332Scy	a1 = ipf_p_ftp_atoi(&s);
330110916Sdarrenr	if (s == NULL) {
331255332Scy		DT2(ftp_PORT_error_atoi_1, nat_t *, nat, ftpside_t *, f);
332255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
333255332Scy			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1);
33453642Sguido		return 0;
335110916Sdarrenr	}
336255332Scy	a2 = ipf_p_ftp_atoi(&s);
337110916Sdarrenr	if (s == NULL) {
338255332Scy		DT2(ftp_PORT_error_atoi_2, nat_t *, nat, ftpside_t *, f);
339255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
340255332Scy			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2);
34153642Sguido		return 0;
342110916Sdarrenr	}
343145522Sdarrenr
34453642Sguido	/*
345145522Sdarrenr	 * Check that IP address in the PORT/PASV reply is the same as the
34653642Sguido	 * sender of the command - prevents using PORT for port scanning.
34753642Sguido	 */
34853642Sguido	a1 <<= 16;
34953642Sguido	a1 |= a2;
350145522Sdarrenr	if (((nat->nat_dir == NAT_OUTBOUND) &&
351255332Scy	     (a1 != ntohl(nat->nat_osrcaddr))) ||
352145522Sdarrenr	    ((nat->nat_dir == NAT_INBOUND) &&
353255332Scy	     (a1 != ntohl(nat->nat_nsrcaddr)))) {
354255332Scy		DT3(ftp_PORT_error_address, nat_t *, nat, ftpside_t *, f,
355255332Scy		    u_int, a1);
356255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
357255332Scy			printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1");
358145522Sdarrenr		return APR_ERR(1);
359110916Sdarrenr	}
36053642Sguido
361255332Scy	a5 = ipf_p_ftp_atoi(&s);
362110916Sdarrenr	if (s == NULL) {
363255332Scy		DT2(ftp_PORT_error_atoi_3, nat_t *, nat, ftpside_t *, f);
364255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
365255332Scy			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3);
36653642Sguido		return 0;
367110916Sdarrenr	}
36853642Sguido	if (*s == ')')
36953642Sguido		s++;
37053642Sguido
37153642Sguido	/*
37253642Sguido	 * check for CR-LF at the end.
37353642Sguido	 */
37453642Sguido	if (*s == '\n')
37553642Sguido		s--;
376255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
377255332Scy		DT2(ftp_PORT_error_no_crlf, nat_t *, nat, ftpside_t *, f);
378255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
379255332Scy			printf("ipf_p_ftp_port:missing %s\n", "cr-lf");
38053642Sguido		return 0;
381110916Sdarrenr	}
382255332Scy	s += 2;
383255332Scy	a6 = a5 & 0xff;
384145522Sdarrenr
385255332Scy	/*
386255332Scy	 * Calculate the source port. Verification of > 1024 is in
387255332Scy	 * ipf_p_ftp_addport.
388255332Scy	 */
38953642Sguido	a5 >>= 8;
39067614Sdarrenr	a5 &= 0xff;
391145522Sdarrenr	sp = a5 << 8 | a6;
392255332Scy
39353642Sguido	/*
39453642Sguido	 * Calculate new address parts for PORT command
39553642Sguido	 */
396145522Sdarrenr	if (nat->nat_dir == NAT_INBOUND)
397255332Scy		a1 = ntohl(nat->nat_ndstaddr);
398145522Sdarrenr	else
399145522Sdarrenr		a1 = ntohl(ip->ip_src.s_addr);
400255332Scy	a1 = ntohl(ip->ip_src.s_addr);
40153642Sguido	a2 = (a1 >> 16) & 0xff;
40253642Sguido	a3 = (a1 >> 8) & 0xff;
40353642Sguido	a4 = a1 & 0xff;
40453642Sguido	a1 >>= 24;
40560857Sdarrenr	olen = s - f->ftps_rptr;
40692685Sdarrenr	/* DO NOT change this to snprintf! */
407145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL)
408145522Sdarrenr	SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
409145522Sdarrenr		 "PORT", a1, a2, a3, a4, a5, a6);
410130886Sdarrenr#else
41155929Sguido	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
41253642Sguido		       "PORT", a1, a2, a3, a4, a5, a6);
413130886Sdarrenr#endif
41453642Sguido
41553642Sguido	nlen = strlen(newbuf);
41653642Sguido	inc = nlen - olen;
417255332Scy	if ((inc + fin->fin_plen) > 65535) {
418255332Scy		DT3(ftp_PORT_error_inc, nat_t *, nat, ftpside_t *, f,
419255332Scy		    int, inc);
420255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
421255332Scy			printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n",
422145522Sdarrenr			       inc);
42360857Sdarrenr		return 0;
424110916Sdarrenr	}
42560857Sdarrenr
42695563Sdarrenr#if !defined(_KERNEL)
427255332Scy	M_ADJ(m, inc);
42895563Sdarrenr#else
429145522Sdarrenr	/*
430145522Sdarrenr	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
431145522Sdarrenr	 * mean remove -len bytes from the end of the packet.
432145522Sdarrenr	 * The mbuf chain will be extended if necessary by m_copyback().
433145522Sdarrenr	 */
434145522Sdarrenr	if (inc < 0)
435255332Scy		M_ADJ(m, inc);
436145522Sdarrenr#endif /* !defined(_KERNEL) */
437145522Sdarrenr	COPYBACK(m, off, nlen, newbuf);
438255332Scy	fin->fin_flx |= FI_DOCKSUM;
439145522Sdarrenr
44053642Sguido	if (inc != 0) {
441255332Scy		fin->fin_plen += inc;
442255332Scy		ip->ip_len = htons(fin->fin_plen);
443145522Sdarrenr		fin->fin_dlen += inc;
44453642Sguido	}
44553642Sguido
446255332Scy	f->ftps_cmd = FTPXY_C_PORT;
447255332Scy	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, sp, inc);
448255332Scy}
449255332Scy
450255332Scy
451255332Scyint
452255332Scyipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, nport, inc)
453255332Scy	ipf_ftp_softc_t *softf;
454255332Scy	fr_info_t *fin;
455255332Scy	ip_t *ip;
456255332Scy	nat_t *nat;
457255332Scy	ftpinfo_t *ftp;
458255332Scy	int dlen, nport, inc;
459255332Scy{
460255332Scy	tcphdr_t tcph, *tcp2 = &tcph;
461255332Scy	ipf_main_softc_t *softc;
462255332Scy	ipf_nat_softc_t *softn;
463255332Scy	int direction;
464255332Scy	fr_info_t fi;
465255332Scy	ipnat_t *ipn;
466255332Scy	nat_t *nat2;
467255332Scy	u_short sp;
468255332Scy	int flags;
469255332Scy
470255332Scy	softc = fin->fin_main_soft;
471255332Scy	softn = softc->ipf_nat_soft;
472255332Scy
473255332Scy	if ((ftp->ftp_pendnat != NULL)  || (ftp->ftp_pendstate != NULL)) {
474255332Scy		if (softf->ipf_p_ftp_single_xfer != 0) {
475255332Scy			DT2(ftp_PORT_error_add_active, nat_t *, nat,
476255332Scy			    ftpinfo_t *, ftp);
477255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
478255332Scy				printf("ipf_p_ftp_addport:xfer active %p/%p\n",
479255332Scy				       ftp->ftp_pendnat, ftp->ftp_pendstate);
480255332Scy			return 0;
481255332Scy		}
482255332Scy		ipf_p_ftp_setpending(softc, ftp);
483255332Scy	}
484255332Scy
48553642Sguido	/*
486255332Scy	 * Add skeleton NAT entry for connection which will come back the
487255332Scy	 * other way.
488255332Scy	 */
489255332Scy	sp = nport;
490255332Scy	/*
491255332Scy	 * Don't allow the PORT command to specify a port < 1024 due to
492255332Scy	 * security risks.
493255332Scy	 */
494255332Scy	if (sp < 1024) {
495255332Scy		DT3(ftp_PORT_error_port, nat_t *, nat, ftpinfo_t *, ftp,
496255332Scy		    u_int, sp);
497255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_SECURITY)
498255332Scy			printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp);
499255332Scy		return 0;
500255332Scy	}
501255332Scy	/*
50253642Sguido	 * The server may not make the connection back from port 20, but
50353642Sguido	 * it is the most likely so use it here to check for a conflicting
50453642Sguido	 * mapping.
50553642Sguido	 */
50692685Sdarrenr	bcopy((char *)fin, (char *)&fi, sizeof(fi));
507145522Sdarrenr	fi.fin_flx |= FI_IGNORE;
50892685Sdarrenr	fi.fin_data[0] = sp;
50992685Sdarrenr	fi.fin_data[1] = fin->fin_data[1] - 1;
510255332Scy	fi.fin_src6 = nat->nat_ndst6;
511255332Scy	fi.fin_dst6 = nat->nat_nsrc6;
512255332Scy
513255332Scy	if (nat->nat_v[0] == 6) {
514255332Scy#ifndef USE_INET6
515255332Scy		return APR_INC(inc);
516255332Scy#endif
517255332Scy	}
518255332Scy
519145522Sdarrenr	/*
520145522Sdarrenr	 * Add skeleton NAT entry for connection which will come back the
521145522Sdarrenr	 * other way.
522145522Sdarrenr	 */
523255332Scy#ifdef USE_INET6
524255332Scy	if (nat->nat_v[0] == 6) {
525145522Sdarrenr		if (nat->nat_dir == NAT_OUTBOUND) {
526255332Scy			nat2 = ipf_nat6_outlookup(&fi, IPN_TCP|NAT_SEARCH,
527255332Scy						  nat->nat_pr[1],
528255332Scy						  &nat->nat_osrc6.in6,
529255332Scy						  &nat->nat_odst6.in6);
530255332Scy		} else {
531255332Scy			nat2 = ipf_nat6_inlookup(&fi, IPN_TCP|NAT_SEARCH,
532255332Scy						 nat->nat_pr[0],
533255332Scy						 &nat->nat_odst6.in6,
534255332Scy						 &nat->nat_osrc6.in6);
53553642Sguido		}
536255332Scy	} else
537255332Scy#endif
538255332Scy	{
539255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
540255332Scy			nat2 = ipf_nat_outlookup(&fi, IPN_TCP|NAT_SEARCH,
541255332Scy						 nat->nat_pr[1],
542255332Scy						 nat->nat_osrcip,
543255332Scy						 nat->nat_odstip);
544255332Scy		} else {
545255332Scy			nat2 = ipf_nat_inlookup(&fi, IPN_TCP|NAT_SEARCH,
546255332Scy						nat->nat_pr[0],
547255332Scy						nat->nat_odstip,
548255332Scy						nat->nat_osrcip);
549255332Scy		}
550255332Scy	}
551255332Scy	if (nat2 != NULL)
552255332Scy		return APR_INC(inc);
553145522Sdarrenr
554255332Scy	ipn = ipf_proxy_rule_rev(nat);
555255332Scy	if (ipn == NULL)
556255332Scy		return APR_ERR(1);
557255332Scy	ipn->in_use = 0;
558145522Sdarrenr
559255332Scy	fi.fin_fr = &ftppxyfr;
560255332Scy	fi.fin_dp = (char *)tcp2;
561255332Scy	fi.fin_dlen = sizeof(*tcp2);
562255332Scy	fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
563255332Scy	fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
564255332Scy	fi.fin_data[1] = sp;
565255332Scy	fi.fin_data[0] = 0;
566255332Scy
567255332Scy	bzero((char *)tcp2, sizeof(*tcp2));
568255332Scy	tcp2->th_sport = 0;
569255332Scy	tcp2->th_dport = htons(sp);
570255332Scy
571255332Scy	tcp2->th_win = htons(8192);
572255332Scy	TCP_OFF_A(tcp2, 5);
573255332Scy	tcp2->th_flags = TH_SYN;
574255332Scy
575255332Scy	if (nat->nat_dir == NAT_INBOUND) {
576255332Scy		fi.fin_out = 1;
577255332Scy		direction = NAT_OUTBOUND;
578255332Scy	} else {
579255332Scy		fi.fin_out = 0;
580255332Scy		direction = NAT_INBOUND;
58153642Sguido	}
582255332Scy	flags = SI_W_SPORT|NAT_SLAVE|IPN_TCP;
583255332Scy
584255332Scy	MUTEX_ENTER(&softn->ipf_nat_new);
585255332Scy	if (nat->nat_v[0] == 6) {
586255332Scy#ifdef USE_INET6
587255332Scy		nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, flags,
588255332Scy				    direction);
589255332Scy#endif
590255332Scy	} else {
591255332Scy		nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, flags,
592255332Scy				   direction);
593255332Scy	}
594255332Scy	MUTEX_EXIT(&softn->ipf_nat_new);
595255332Scy
596255332Scy	if (nat2 == NULL) {
597255332Scy		KFREES(ipn, ipn->in_size);
598255332Scy		return APR_ERR(1);
599255332Scy	}
600255332Scy
601255332Scy	(void) ipf_nat_proto(&fi, nat2, IPN_TCP);
602255332Scy	MUTEX_ENTER(&nat2->nat_lock);
603255332Scy	ipf_nat_update(&fi, nat2);
604255332Scy	MUTEX_EXIT(&nat2->nat_lock);
605255332Scy	fi.fin_ifp = NULL;
606255332Scy	if (nat2->nat_dir == NAT_INBOUND)
607255332Scy		fi.fin_dst6 = nat->nat_osrc6;
608255332Scy	if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate,
609255332Scy			  SI_W_SPORT) != 0)
610255332Scy		ipf_nat_setpending(softc, nat2);
611255332Scy
612145522Sdarrenr	return APR_INC(inc);
61353642Sguido}
61453642Sguido
61553642Sguido
616255332Scyint
617255332Scyipf_p_ftp_client(softf, fin, ip, nat, ftp, dlen)
618255332Scy	ipf_ftp_softc_t *softf;
619255332Scy	fr_info_t *fin;
620255332Scy	nat_t *nat;
621255332Scy	ftpinfo_t *ftp;
622255332Scy	ip_t *ip;
623255332Scy	int dlen;
62453642Sguido{
62563523Sdarrenr	char *rptr, *wptr, cmd[6], c;
62660857Sdarrenr	ftpside_t *f;
62763523Sdarrenr	int inc, i;
62860857Sdarrenr
62960857Sdarrenr	inc = 0;
63060857Sdarrenr	f = &ftp->ftp_side[0];
63160857Sdarrenr	rptr = f->ftps_rptr;
63260857Sdarrenr	wptr = f->ftps_wptr;
63360857Sdarrenr
63463523Sdarrenr	for (i = 0; (i < 5) && (i < dlen); i++) {
63563523Sdarrenr		c = rptr[i];
636145522Sdarrenr		if (ISALPHA(c)) {
637145522Sdarrenr			cmd[i] = TOUPPER(c);
63863523Sdarrenr		} else {
63963523Sdarrenr			cmd[i] = c;
64063523Sdarrenr		}
64163523Sdarrenr	}
64263523Sdarrenr	cmd[i] = '\0';
64363523Sdarrenr
64480482Sdarrenr	ftp->ftp_incok = 0;
645255332Scy	DT2(ftp_client_command, char [], cmd, int, ftp->ftp_passok);
64680482Sdarrenr	if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
64780482Sdarrenr		if (ftp->ftp_passok == FTPXY_ADOK_1 ||
64880482Sdarrenr		    ftp->ftp_passok == FTPXY_AUOK_1) {
64980482Sdarrenr			ftp->ftp_passok = FTPXY_USER_2;
65080482Sdarrenr			ftp->ftp_incok = 1;
65180482Sdarrenr		} else {
65280482Sdarrenr			ftp->ftp_passok = FTPXY_USER_1;
65380482Sdarrenr			ftp->ftp_incok = 1;
65480482Sdarrenr		}
65580482Sdarrenr	} else if (!strncmp(cmd, "AUTH ", 5)) {
65680482Sdarrenr		ftp->ftp_passok = FTPXY_AUTH_1;
65780482Sdarrenr		ftp->ftp_incok = 1;
65880482Sdarrenr	} else if (!strncmp(cmd, "PASS ", 5)) {
65980482Sdarrenr		if (ftp->ftp_passok == FTPXY_USOK_1) {
66080482Sdarrenr			ftp->ftp_passok = FTPXY_PASS_1;
66180482Sdarrenr			ftp->ftp_incok = 1;
66280482Sdarrenr		} else if (ftp->ftp_passok == FTPXY_USOK_2) {
66380482Sdarrenr			ftp->ftp_passok = FTPXY_PASS_2;
66480482Sdarrenr			ftp->ftp_incok = 1;
66580482Sdarrenr		}
66680482Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
66780482Sdarrenr		   !strncmp(cmd, "ADAT ", 5)) {
66880482Sdarrenr		ftp->ftp_passok = FTPXY_ADAT_1;
66980482Sdarrenr		ftp->ftp_incok = 1;
67092685Sdarrenr	} else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
67192685Sdarrenr		    ftp->ftp_passok == FTPXY_PAOK_2) &&
67280482Sdarrenr		 !strncmp(cmd, "ACCT ", 5)) {
67380482Sdarrenr		ftp->ftp_passok = FTPXY_ACCT_1;
67480482Sdarrenr		ftp->ftp_incok = 1;
675255332Scy	} else if ((ftp->ftp_passok == FTPXY_GO) &&
676255332Scy		   !softf->ipf_p_ftp_pasvonly &&
67763523Sdarrenr		 !strncmp(cmd, "PORT ", 5)) {
678255332Scy		inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
679255332Scy	} else if ((ftp->ftp_passok == FTPXY_GO) &&
680255332Scy		   !softf->ipf_p_ftp_pasvonly &&
681255332Scy		 !strncmp(cmd, "EPRT ", 5)) {
682255332Scy		inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen);
683255332Scy	} else if (softf->ipf_p_ftp_insecure &&
684255332Scy		   !softf->ipf_p_ftp_pasvonly &&
68563523Sdarrenr		   !strncmp(cmd, "PORT ", 5)) {
686255332Scy		inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
68760857Sdarrenr	}
688255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
689255332Scy		printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n",
690255332Scy		       cmd, ftp->ftp_passok, ftp->ftp_incok, inc);
69160857Sdarrenr
692255332Scy	DT2(ftp_client_passok, char *, cmd, int, ftp->ftp_passok);
69360857Sdarrenr	while ((*rptr++ != '\n') && (rptr < wptr))
69460857Sdarrenr		;
69560857Sdarrenr	f->ftps_rptr = rptr;
69660857Sdarrenr	return inc;
69753642Sguido}
69853642Sguido
69953642Sguido
700255332Scyint
701255332Scyipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen)
702255332Scy	ipf_ftp_softc_t *softf;
703255332Scy	fr_info_t *fin;
704255332Scy	ip_t *ip;
705255332Scy	nat_t *nat;
706255332Scy	ftpinfo_t *ftp;
707255332Scy	int dlen;
70853642Sguido{
709145522Sdarrenr	u_int a1, a2, a3, a4, data_ip;
710145522Sdarrenr	char newbuf[IPF_FTPBUFSZ];
711153876Sguido	const char *brackets[2];
712145522Sdarrenr	u_short a5, a6;
713110916Sdarrenr	ftpside_t *f;
714153876Sguido	char *s;
71553642Sguido
716255332Scy	if ((softf->ipf_p_ftp_forcepasv != 0) &&
717255332Scy	    (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) {
718255332Scy		DT2(ftp_PASV_error_state, nat_t *, nat, ftpinfo_t *, ftp);
719255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
720255332Scy			printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n",
721255332Scy			       ftp->ftp_side[0].ftps_cmd);
722110916Sdarrenr		return 0;
723110916Sdarrenr	}
724110916Sdarrenr
725110916Sdarrenr	f = &ftp->ftp_side[1];
726110916Sdarrenr
72780482Sdarrenr#define	PASV_REPLEN	24
72860857Sdarrenr	/*
72960857Sdarrenr	 * Check for PASV reply message.
73060857Sdarrenr	 */
731110916Sdarrenr	if (dlen < IPF_MIN227LEN) {
732255332Scy		DT3(ftp_PASV_error_short, nat_t *, nat, ftpinfo_t *, ftp,
733255332Scy		    int, dlen);
734255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
735255332Scy			printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
736145522Sdarrenr			       dlen);
73760857Sdarrenr		return 0;
738110916Sdarrenr	} else if (strncmp(f->ftps_rptr,
739110916Sdarrenr			   "227 Entering Passive Mod", PASV_REPLEN)) {
740255332Scy		DT2(ftp_PASV_error_string, nat_t *, nat, ftpinfo_t *, ftp);
741255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
742255332Scy			printf("ipf_p_ftp_pasv:%d reply wrong\n", 227);
74360857Sdarrenr		return 0;
744110916Sdarrenr	}
74560857Sdarrenr
746145522Sdarrenr	brackets[0] = "";
747145522Sdarrenr	brackets[1] = "";
74853642Sguido	/*
749110916Sdarrenr	 * Skip the PASV reply + space
75053642Sguido	 */
75180482Sdarrenr	s = f->ftps_rptr + PASV_REPLEN;
752145522Sdarrenr	while (*s && !ISDIGIT(*s)) {
753145522Sdarrenr		if (*s == '(') {
754145522Sdarrenr			brackets[0] = "(";
755145522Sdarrenr			brackets[1] = ")";
756145522Sdarrenr		}
75753642Sguido		s++;
758145522Sdarrenr	}
759145522Sdarrenr
76053642Sguido	/*
76153642Sguido	 * Pick out the address components, two at a time.
76253642Sguido	 */
763255332Scy	a1 = ipf_p_ftp_atoi(&s);
764110916Sdarrenr	if (s == NULL) {
765255332Scy		DT2(ftp_PASV_error_atoi_1, nat_t *, nat, ftpside_t *, f);
766255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
767255332Scy			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1);
76853642Sguido		return 0;
769110916Sdarrenr	}
770255332Scy	a2 = ipf_p_ftp_atoi(&s);
771110916Sdarrenr	if (s == NULL) {
772255332Scy		DT2(ftp_PASV_error_atoi_2, nat_t *, nat, ftpside_t *, f);
773255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
774255332Scy			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2);
77553642Sguido		return 0;
776110916Sdarrenr	}
77753642Sguido
77853642Sguido	/*
779145522Sdarrenr	 * check that IP address in the PASV reply is the same as the
780145522Sdarrenr	 * sender of the command - prevents using PASV for port scanning.
78153642Sguido	 */
78253642Sguido	a1 <<= 16;
78353642Sguido	a1 |= a2;
784145522Sdarrenr
785145522Sdarrenr	if (((nat->nat_dir == NAT_INBOUND) &&
786255332Scy	     (a1 != ntohl(nat->nat_ndstaddr))) ||
787145522Sdarrenr	    ((nat->nat_dir == NAT_OUTBOUND) &&
788255332Scy	     (a1 != ntohl(nat->nat_odstaddr)))) {
789255332Scy		DT3(ftp_PASV_error_address, nat_t *, nat, ftpside_t *, f,
790255332Scy		    u_int, a1);
791255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
792255332Scy			printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1");
79353642Sguido		return 0;
794110916Sdarrenr	}
79553642Sguido
796255332Scy	a5 = ipf_p_ftp_atoi(&s);
797110916Sdarrenr	if (s == NULL) {
798255332Scy		DT2(ftp_PASV_error_atoi_3, nat_t *, nat, ftpside_t *, f);
799255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
800255332Scy			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3);
80153642Sguido		return 0;
802110916Sdarrenr	}
80353642Sguido
80453642Sguido	if (*s == ')')
80553642Sguido		s++;
80680482Sdarrenr	if (*s == '.')
80780482Sdarrenr		s++;
80853642Sguido	if (*s == '\n')
80953642Sguido		s--;
81053642Sguido	/*
81153642Sguido	 * check for CR-LF at the end.
81253642Sguido	 */
813255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
814255332Scy		DT(pasv_missing_crlf);
815255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
816255332Scy			printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n");
81753642Sguido		return 0;
818110916Sdarrenr	}
819255332Scy	s += 2;
820145522Sdarrenr
821145522Sdarrenr	a6 = a5 & 0xff;
82253642Sguido	a5 >>= 8;
82353642Sguido	/*
82453642Sguido	 * Calculate new address parts for 227 reply
82553642Sguido	 */
826145522Sdarrenr	if (nat->nat_dir == NAT_INBOUND) {
827255332Scy		data_ip = nat->nat_odstaddr;
828145522Sdarrenr		a1 = ntohl(data_ip);
829145522Sdarrenr	} else
830145522Sdarrenr		data_ip = htonl(a1);
831145522Sdarrenr
83253642Sguido	a2 = (a1 >> 16) & 0xff;
83353642Sguido	a3 = (a1 >> 8) & 0xff;
83453642Sguido	a4 = a1 & 0xff;
83553642Sguido	a1 >>= 24;
836145522Sdarrenr
837145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL)
838145522Sdarrenr	SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
839145522Sdarrenr		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
840145522Sdarrenr		a5, a6, brackets[1]);
841145522Sdarrenr#else
842145522Sdarrenr	(void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
843145522Sdarrenr		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
844145522Sdarrenr		a5, a6, brackets[1]);
845145522Sdarrenr#endif
846255332Scy	return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6),
847255332Scy				   newbuf, s);
848145522Sdarrenr}
849145522Sdarrenr
850255332Scyint
851255332Scyipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, port, newmsg, s)
852255332Scy	ipf_ftp_softc_t *softf;
853255332Scy	fr_info_t *fin;
854255332Scy	ip_t *ip;
855255332Scy	nat_t *nat;
856255332Scy	ftpinfo_t *ftp;
857255332Scy	u_int port;
858255332Scy	char *newmsg;
859255332Scy	char *s;
860145522Sdarrenr{
861255332Scy	int inc, off, nflags;
862145522Sdarrenr	tcphdr_t *tcp, tcph, *tcp2;
863255332Scy	ipf_main_softc_t *softc;
864255332Scy	ipf_nat_softc_t *softn;
865145522Sdarrenr	size_t nlen, olen;
866255332Scy#ifdef USE_INET6
867255332Scy	ip6_t *ip6;
868255332Scy#endif
869255332Scy	ipnat_t *ipn;
870145522Sdarrenr	fr_info_t fi;
871255332Scy	ftpside_t *f;
872145522Sdarrenr	nat_t *nat2;
873145522Sdarrenr	mb_t *m;
874145522Sdarrenr
875255332Scy	softc = fin->fin_main_soft;
876255332Scy	softn = softc->ipf_nat_soft;
877255332Scy
878255332Scy	if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL))
879255332Scy		ipf_p_ftp_setpending(softc, ftp);
880255332Scy
881145522Sdarrenr	m = fin->fin_m;
882145522Sdarrenr	tcp = (tcphdr_t *)fin->fin_dp;
883145522Sdarrenr	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
884145522Sdarrenr
885145522Sdarrenr	tcp2 = &tcph;
88660857Sdarrenr	inc = 0;
887145522Sdarrenr
888255332Scy	f = &ftp->ftp_side[1];
88960857Sdarrenr	olen = s - f->ftps_rptr;
890145522Sdarrenr	nlen = strlen(newmsg);
89153642Sguido	inc = nlen - olen;
892255332Scy	if ((inc + fin->fin_plen) > 65535) {
893255332Scy		DT3(ftp_PASV_error_inc, nat_t *, nat, ftpside_t *, f,
894255332Scy		    int, inc);
895255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
896255332Scy			printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
897145522Sdarrenr			       inc);
89860857Sdarrenr		return 0;
899145522Sdarrenr	}
90060857Sdarrenr
901255332Scy	ipn = ipf_proxy_rule_fwd(nat);
902255332Scy	if (ipn == NULL)
903255332Scy		return APR_ERR(1);
904255332Scy	ipn->in_use = 0;
905145522Sdarrenr
90653642Sguido	/*
90753642Sguido	 * Add skeleton NAT entry for connection which will come back the
90853642Sguido	 * other way.
90953642Sguido	 */
910255332Scy	bzero((char *)tcp2, sizeof(*tcp2));
91192685Sdarrenr	bcopy((char *)fin, (char *)&fi, sizeof(fi));
912145522Sdarrenr	fi.fin_flx |= FI_IGNORE;
91392685Sdarrenr	fi.fin_data[0] = 0;
914145522Sdarrenr	fi.fin_data[1] = port;
915145522Sdarrenr	nflags = IPN_TCP|SI_W_SPORT;
916255332Scy
917255332Scy	fi.fin_fr = &ftppxyfr;
918255332Scy	fi.fin_dp = (char *)tcp2;
919255332Scy	fi.fin_out = 1 - fin->fin_out;
920255332Scy	fi.fin_dlen = sizeof(*tcp2);
921255332Scy	fi.fin_src6 = nat->nat_osrc6;
922255332Scy	fi.fin_dst6 = nat->nat_odst6;
923255332Scy	fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
924255332Scy	fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
925255332Scy
926255332Scy	TCP_OFF_A(tcp2, 5);
927255332Scy	tcp2->th_flags = TH_SYN;
928255332Scy	tcp2->th_win = htons(8192);
929255332Scy	tcp2->th_dport = htons(port);
930255332Scy
931255332Scy	MUTEX_ENTER(&softn->ipf_nat_new);
932255332Scy#ifdef USE_INET6
933255332Scy	if (nat->nat_v[0] == 6)
934255332Scy		nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat,
935255332Scy				    nflags, nat->nat_dir);
936145522Sdarrenr	else
937255332Scy#endif
938255332Scy		nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat,
939255332Scy				   nflags, nat->nat_dir);
940255332Scy	MUTEX_EXIT(&softn->ipf_nat_new);
941255332Scy
942145522Sdarrenr	if (nat2 == NULL) {
943255332Scy		KFREES(ipn, ipn->in_size);
944255332Scy		return APR_ERR(1);
945255332Scy	}
94660857Sdarrenr
947255332Scy	(void) ipf_nat_proto(&fi, nat2, IPN_TCP);
948255332Scy	MUTEX_ENTER(&nat2->nat_lock);
949255332Scy	ipf_nat_update(&fi, nat2);
950255332Scy	MUTEX_EXIT(&nat2->nat_lock);
951255332Scy	fi.fin_ifp = NULL;
952255332Scy	if (nat->nat_dir == NAT_INBOUND) {
953255332Scy		if (nat->nat_v[0] == 6) {
954255332Scy#ifdef USE_INET6
955255332Scy			fi.fin_dst6 = nat->nat_ndst6;
956255332Scy#endif
957255332Scy		} else {
958255332Scy			fi.fin_daddr = nat->nat_ndstaddr;
95953642Sguido		}
960255332Scy	}
961255332Scy	if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate,
962255332Scy			  SI_W_SPORT) != 0)
963255332Scy		ipf_nat_setpending(softc, nat2);
964145522Sdarrenr
965255332Scy#if !defined(_KERNEL)
966255332Scy	M_ADJ(m, inc);
967255332Scy#else
968255332Scy	/*
969255332Scy	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
970255332Scy	 * mean remove -len bytes from the end of the packet.
971255332Scy	 * The mbuf chain will be extended if necessary by m_copyback().
972255332Scy	 */
973255332Scy	if (inc < 0)
974255332Scy		M_ADJ(m, inc);
975255332Scy#endif /* !defined(_KERNEL) */
976255332Scy	COPYBACK(m, off, nlen, newmsg);
977255332Scy	fin->fin_flx |= FI_DOCKSUM;
978255332Scy
979255332Scy	if (inc != 0) {
980255332Scy		fin->fin_plen += inc;
981255332Scy		fin->fin_dlen += inc;
982255332Scy		if (nat->nat_v[0] == 6) {
983255332Scy#ifdef USE_INET6
984255332Scy			ip6 = (ip6_t *)fin->fin_ip;
985255332Scy			u_short len = ntohs(ip6->ip6_plen) + inc;
986255332Scy			ip6->ip6_plen = htons(len);
987255332Scy#endif
988255332Scy		} else {
989255332Scy			ip->ip_len = htons(fin->fin_plen);
990145522Sdarrenr		}
991255332Scy	}
992145522Sdarrenr
993255332Scy	return APR_INC(inc);
99453642Sguido}
99553642Sguido
99653642Sguido
997255332Scyint
998255332Scyipf_p_ftp_server(softf, fin, ip, nat, ftp, dlen)
999255332Scy	ipf_ftp_softc_t *softf;
1000255332Scy	fr_info_t *fin;
1001255332Scy	ip_t *ip;
1002255332Scy	nat_t *nat;
1003255332Scy	ftpinfo_t *ftp;
1004255332Scy	int dlen;
100560857Sdarrenr{
100660857Sdarrenr	char *rptr, *wptr;
100760857Sdarrenr	ftpside_t *f;
100860857Sdarrenr	int inc;
100960857Sdarrenr
101060857Sdarrenr	inc = 0;
101160857Sdarrenr	f = &ftp->ftp_side[1];
101260857Sdarrenr	rptr = f->ftps_rptr;
101360857Sdarrenr	wptr = f->ftps_wptr;
101460857Sdarrenr
1015255332Scy	DT2(ftp_server_response, char *, rptr, int, ftp->ftp_passok);
1016145522Sdarrenr	if (*rptr == ' ')
1017145522Sdarrenr		goto server_cmd_ok;
1018145522Sdarrenr	if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
1019102520Sdarrenr		return 0;
1020255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1021255332Scy		printf("ipf_p_ftp_server_1: cmd[%4.4s] passok %d\n",
1022255332Scy		       rptr, ftp->ftp_passok);
102380482Sdarrenr	if (ftp->ftp_passok == FTPXY_GO) {
102480482Sdarrenr		if (!strncmp(rptr, "227 ", 4))
1025255332Scy			inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
1026145522Sdarrenr		else if (!strncmp(rptr, "229 ", 4))
1027255332Scy			inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
1028255332Scy		else if (strncmp(rptr, "200", 3)) {
1029255332Scy			/*
1030255332Scy			 * 200 is returned for a successful command.
1031255332Scy			 */
1032255332Scy			;
1033255332Scy		}
1034255332Scy	} else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
1035255332Scy		inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
1036255332Scy	} else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
1037255332Scy		inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
103880482Sdarrenr	} else if (*rptr == '5' || *rptr == '4')
103980482Sdarrenr		ftp->ftp_passok = FTPXY_INIT;
104080482Sdarrenr	else if (ftp->ftp_incok) {
104180482Sdarrenr		if (*rptr == '3') {
104280482Sdarrenr			if (ftp->ftp_passok == FTPXY_ACCT_1)
104380482Sdarrenr				ftp->ftp_passok = FTPXY_GO;
104480482Sdarrenr			else
104580482Sdarrenr				ftp->ftp_passok++;
104680482Sdarrenr		} else if (*rptr == '2') {
104780482Sdarrenr			switch (ftp->ftp_passok)
104880482Sdarrenr			{
104980482Sdarrenr			case FTPXY_USER_1 :
105080482Sdarrenr			case FTPXY_USER_2 :
105180482Sdarrenr			case FTPXY_PASS_1 :
105280482Sdarrenr			case FTPXY_PASS_2 :
105380482Sdarrenr			case FTPXY_ACCT_1 :
105480482Sdarrenr				ftp->ftp_passok = FTPXY_GO;
105580482Sdarrenr				break;
105680482Sdarrenr			default :
105780482Sdarrenr				ftp->ftp_passok += 3;
105880482Sdarrenr				break;
105980482Sdarrenr			}
106080482Sdarrenr		}
106160857Sdarrenr	}
1062255332Scy	ftp->ftp_incok = 0;
1063145522Sdarrenrserver_cmd_ok:
1064255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1065255332Scy		printf("ipf_p_ftp_server_2: cmd[%4.4s] passok %d\n",
1066255332Scy		       rptr, ftp->ftp_passok);
1067255332Scy	DT3(ftp_server_passok, char *,rptr, int, ftp->ftp_incok,
1068255332Scy	    int, ftp->ftp_passok);
1069110916Sdarrenr
107060857Sdarrenr	while ((*rptr++ != '\n') && (rptr < wptr))
107160857Sdarrenr		;
107260857Sdarrenr	f->ftps_rptr = rptr;
107360857Sdarrenr	return inc;
107460857Sdarrenr}
107560857Sdarrenr
107660857Sdarrenr
107760857Sdarrenr/*
1078255332Scy * 0 FTPXY_JUNK_OK
1079255332Scy * 1 FTPXY_JUNK_BAD
1080255332Scy * 2 FTPXY_JUNK_EOL
1081255332Scy * 3 FTPXY_JUNK_CONT
1082255332Scy *
108360857Sdarrenr * Look to see if the buffer starts with something which we recognise as
108460857Sdarrenr * being the correct syntax for the FTP protocol.
108560857Sdarrenr */
1086255332Scyint
1087255332Scyipf_p_ftp_client_valid(softf, ftps, buf, len)
1088255332Scy	ipf_ftp_softc_t *softf;
1089255332Scy	ftpside_t *ftps;
1090255332Scy	char *buf;
1091255332Scy	size_t len;
109260857Sdarrenr{
1093145522Sdarrenr	register char *s, c, pc;
109460857Sdarrenr	register size_t i = len;
1095110916Sdarrenr	char cmd[5];
109660857Sdarrenr
1097145522Sdarrenr	s = buf;
1098145522Sdarrenr
1099255332Scy	if (ftps->ftps_junk == FTPXY_JUNK_BAD)
1100255332Scy		return FTPXY_JUNK_BAD;
1101145522Sdarrenr
1102110916Sdarrenr	if (i < 5) {
1103255332Scy		DT1(client_valid, int, i);
1104255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1105255332Scy			printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i);
110660857Sdarrenr		return 2;
1107110916Sdarrenr	}
1108145522Sdarrenr
1109145522Sdarrenr	i--;
111060857Sdarrenr	c = *s++;
111160857Sdarrenr
1112145522Sdarrenr	if (ISALPHA(c)) {
1113145522Sdarrenr		cmd[0] = TOUPPER(c);
111460857Sdarrenr		c = *s++;
111560857Sdarrenr		i--;
1116145522Sdarrenr		if (ISALPHA(c)) {
1117145522Sdarrenr			cmd[1] = TOUPPER(c);
111860857Sdarrenr			c = *s++;
111960857Sdarrenr			i--;
1120145522Sdarrenr			if (ISALPHA(c)) {
1121145522Sdarrenr				cmd[2] = TOUPPER(c);
112260857Sdarrenr				c = *s++;
112360857Sdarrenr				i--;
1124145522Sdarrenr				if (ISALPHA(c)) {
1125145522Sdarrenr					cmd[3] = TOUPPER(c);
112692685Sdarrenr					c = *s++;
112792685Sdarrenr					i--;
112892685Sdarrenr					if ((c != ' ') && (c != '\r'))
1129110916Sdarrenr						goto bad_client_command;
113092685Sdarrenr				} else if ((c != ' ') && (c != '\r'))
1131110916Sdarrenr					goto bad_client_command;
113260857Sdarrenr			} else
1133110916Sdarrenr				goto bad_client_command;
113460857Sdarrenr		} else
1135110916Sdarrenr			goto bad_client_command;
1136110916Sdarrenr	} else {
1137110916Sdarrenrbad_client_command:
1138255332Scy		DT4(client_junk, int, len, int, i, int, c, char *, buf);
1139255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1140145522Sdarrenr			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
1141255332Scy			       "ipf_p_ftp_client_valid",
1142145522Sdarrenr			       ftps->ftps_junk, (int)len, (int)i, c,
1143145522Sdarrenr			       (int)len, (int)len, buf);
1144255332Scy		return FTPXY_JUNK_BAD;
1145110916Sdarrenr	}
1146110916Sdarrenr
114792685Sdarrenr	for (; i; i--) {
1148145522Sdarrenr		pc = c;
114960857Sdarrenr		c = *s++;
1150145522Sdarrenr		if ((pc == '\r') && (c == '\n')) {
1151110916Sdarrenr			cmd[4] = '\0';
1152255332Scy			if (!strcmp(cmd, "PASV")) {
1153255332Scy				ftps->ftps_cmd = FTPXY_C_PASV;
1154255332Scy			} else if (!strcmp(cmd, "EPSV")) {
1155255332Scy				ftps->ftps_cmd = FTPXY_C_EPSV;
1156255332Scy			} else {
1157255332Scy				ftps->ftps_cmd = 0;
1158255332Scy			}
115992685Sdarrenr			return 0;
1160110916Sdarrenr		}
116192685Sdarrenr	}
1162145522Sdarrenr#if !defined(_KERNEL)
1163255332Scy	printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n",
1164145522Sdarrenr	       (int)len, (int)len, buf);
1165110916Sdarrenr#endif
1166255332Scy	return FTPXY_JUNK_EOL;
116792685Sdarrenr}
116892685Sdarrenr
116992685Sdarrenr
1170255332Scyint
1171255332Scyipf_p_ftp_server_valid(softf, ftps, buf, len)
1172255332Scy	ipf_ftp_softc_t *softf;
1173255332Scy	ftpside_t *ftps;
1174255332Scy	char *buf;
1175255332Scy	size_t len;
117692685Sdarrenr{
1177145522Sdarrenr	register char *s, c, pc;
117892685Sdarrenr	register size_t i = len;
1179110916Sdarrenr	int cmd;
118092685Sdarrenr
1181145522Sdarrenr	s = buf;
1182145522Sdarrenr	cmd = 0;
1183145522Sdarrenr
1184255332Scy	if (ftps->ftps_junk == FTPXY_JUNK_BAD)
1185255332Scy		return FTPXY_JUNK_BAD;
1186145522Sdarrenr
1187145522Sdarrenr	if (i < 5) {
1188255332Scy		DT1(server_valid, int, i);
1189255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1190255332Scy			printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i);
119192685Sdarrenr		return 2;
1192145522Sdarrenr	}
1193145522Sdarrenr
119492685Sdarrenr	c = *s++;
119592685Sdarrenr	i--;
1196255332Scy	if (c == ' ') {
1197255332Scy		cmd = -1;
1198145522Sdarrenr		goto search_eol;
1199255332Scy	}
120092685Sdarrenr
1201145522Sdarrenr	if (ISDIGIT(c)) {
1202110916Sdarrenr		cmd = (c - '0') * 100;
120392685Sdarrenr		c = *s++;
120460857Sdarrenr		i--;
1205145522Sdarrenr		if (ISDIGIT(c)) {
1206110916Sdarrenr			cmd += (c - '0') * 10;
120760857Sdarrenr			c = *s++;
120860857Sdarrenr			i--;
1209145522Sdarrenr			if (ISDIGIT(c)) {
1210110916Sdarrenr				cmd += (c - '0');
121160857Sdarrenr				c = *s++;
121260857Sdarrenr				i--;
121392685Sdarrenr				if ((c != '-') && (c != ' '))
1214110916Sdarrenr					goto bad_server_command;
1215255332Scy				if (c == '-')
1216255332Scy					return FTPXY_JUNK_CONT;
121760857Sdarrenr			} else
1218110916Sdarrenr				goto bad_server_command;
121960857Sdarrenr		} else
1220110916Sdarrenr			goto bad_server_command;
1221110916Sdarrenr	} else {
1222110916Sdarrenrbad_server_command:
1223255332Scy		DT4(server_junk, int len, buf, int, i, int, c, char *, buf);
1224255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO)
1225145522Sdarrenr			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
1226255332Scy			       "ipf_p_ftp_server_valid",
1227145522Sdarrenr			       ftps->ftps_junk, (int)len, (int)i,
1228145522Sdarrenr			       c, (int)len, (int)len, buf);
1229255332Scy		if (ftps->ftps_junk == FTPXY_JUNK_CONT)
1230255332Scy			return FTPXY_JUNK_CONT;
1231255332Scy		return FTPXY_JUNK_BAD;
1232110916Sdarrenr	}
1233145522Sdarrenrsearch_eol:
123460857Sdarrenr	for (; i; i--) {
1235145522Sdarrenr		pc = c;
123660857Sdarrenr		c = *s++;
1237145522Sdarrenr		if ((pc == '\r') && (c == '\n')) {
1238255332Scy			if (cmd == -1) {
1239255332Scy				if (ftps->ftps_junk == FTPXY_JUNK_CONT)
1240255332Scy					return FTPXY_JUNK_CONT;
1241255332Scy			} else {
1242255332Scy				ftps->ftps_cmd = cmd;
1243255332Scy			}
1244255332Scy			return FTPXY_JUNK_OK;
1245110916Sdarrenr		}
124660857Sdarrenr	}
1247255332Scy
1248255332Scy	DT2(junk_eol, int, len, char *, buf);
1249255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO)
1250255332Scy		printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n",
1251145522Sdarrenr		       (int)len, (int)len, buf);
1252255332Scy	return FTPXY_JUNK_EOL;
125360857Sdarrenr}
125460857Sdarrenr
125560857Sdarrenr
1256255332Scyint
1257255332Scyipf_p_ftp_valid(softf, ftp, side, buf, len)
1258255332Scy	ipf_ftp_softc_t *softf;
1259255332Scy	ftpinfo_t *ftp;
1260255332Scy	int side;
1261255332Scy	char *buf;
1262255332Scy	size_t len;
126392685Sdarrenr{
1264110916Sdarrenr	ftpside_t *ftps;
126592685Sdarrenr	int ret;
126692685Sdarrenr
1267110916Sdarrenr	ftps = &ftp->ftp_side[side];
1268110916Sdarrenr
126992685Sdarrenr	if (side == 0)
1270255332Scy		ret = ipf_p_ftp_client_valid(softf, ftps, buf, len);
127192685Sdarrenr	else
1272255332Scy		ret = ipf_p_ftp_server_valid(softf, ftps, buf, len);
127392685Sdarrenr	return ret;
127492685Sdarrenr}
127592685Sdarrenr
127692685Sdarrenr
1277102520Sdarrenr/*
1278145522Sdarrenr * For map rules, the following applies:
1279102520Sdarrenr * rv == 0 for outbound processing,
1280102520Sdarrenr * rv == 1 for inbound processing.
1281145522Sdarrenr * For rdr rules, the following applies:
1282145522Sdarrenr * rv == 0 for inbound processing,
1283145522Sdarrenr * rv == 1 for outbound processing.
1284102520Sdarrenr */
1285255332Scyint
1286255332Scyipf_p_ftp_process(softf, fin, nat, ftp, rv)
1287255332Scy	ipf_ftp_softc_t *softf;
1288255332Scy	fr_info_t *fin;
1289255332Scy	nat_t *nat;
1290255332Scy	ftpinfo_t *ftp;
1291255332Scy	int rv;
129260857Sdarrenr{
1293255332Scy	int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry;
1294145522Sdarrenr	char *rptr, *wptr, *s;
1295102520Sdarrenr	u_32_t thseq, thack;
1296102520Sdarrenr	ap_session_t *aps;
129763523Sdarrenr	ftpside_t *f, *t;
129860857Sdarrenr	tcphdr_t *tcp;
1299145522Sdarrenr	ip_t *ip;
130060857Sdarrenr	mb_t *m;
130160857Sdarrenr
1302145522Sdarrenr	m = fin->fin_m;
1303145522Sdarrenr	ip = fin->fin_ip;
130460857Sdarrenr	tcp = (tcphdr_t *)fin->fin_dp;
1305145522Sdarrenr	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
130660857Sdarrenr
1307145522Sdarrenr	f = &ftp->ftp_side[rv];
1308145522Sdarrenr	t = &ftp->ftp_side[1 - rv];
1309145522Sdarrenr	thseq = ntohl(tcp->th_seq);
1310145522Sdarrenr	thack = ntohl(tcp->th_ack);
1311145522Sdarrenr#ifdef __sgi
1312145522Sdarrenr	mlen = fin->fin_plen - off;
131360857Sdarrenr#else
1314145522Sdarrenr	mlen = MSGDSIZE(m) - off;
131560857Sdarrenr#endif
131672006Sdarrenr
1317255332Scy	DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen);
1318255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1319255332Scy		printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n",
1320255332Scy		       fin->fin_out, fin->fin_sport, fin->fin_dport,
1321255332Scy		       mlen, tcp->th_flags);
1322255332Scy
1323161356Sguido	if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) {
1324161356Sguido		f->ftps_seq[0] = thseq + 1;
1325161356Sguido		t->ftps_seq[0] = thack;
1326145522Sdarrenr		return 0;
1327161356Sguido	} else if (mlen < 0) {
1328161356Sguido		return 0;
1329145522Sdarrenr	}
1330161356Sguido
1331102520Sdarrenr	aps = nat->nat_aps;
133260857Sdarrenr
1333102520Sdarrenr	sel = aps->aps_sel[1 - rv];
1334102520Sdarrenr	sel2 = aps->aps_sel[rv];
1335255332Scy	if (rv == 1) {
1336102520Sdarrenr		seqoff = aps->aps_seqoff[sel];
1337102520Sdarrenr		if (aps->aps_seqmin[sel] > seqoff + thseq)
1338102520Sdarrenr			seqoff = aps->aps_seqoff[!sel];
1339102520Sdarrenr		ackoff = aps->aps_ackoff[sel2];
1340102520Sdarrenr		if (aps->aps_ackmin[sel2] > ackoff + thack)
1341102520Sdarrenr			ackoff = aps->aps_ackoff[!sel2];
1342102520Sdarrenr	} else {
1343102520Sdarrenr		seqoff = aps->aps_ackoff[sel];
1344255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1345145522Sdarrenr			printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1346145522Sdarrenr			       aps->aps_ackmin[sel]);
1347102520Sdarrenr		if (aps->aps_ackmin[sel] > seqoff + thseq)
1348102520Sdarrenr			seqoff = aps->aps_ackoff[!sel];
134960857Sdarrenr
1350102520Sdarrenr		ackoff = aps->aps_seqoff[sel2];
1351255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1352145522Sdarrenr			printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1353145522Sdarrenr			       aps->aps_seqmin[sel2]);
1354102520Sdarrenr		if (ackoff > 0) {
1355102520Sdarrenr			if (aps->aps_seqmin[sel2] > ackoff + thack)
1356102520Sdarrenr				ackoff = aps->aps_seqoff[!sel2];
1357102520Sdarrenr		} else {
1358102520Sdarrenr			if (aps->aps_seqmin[sel2] > thack)
1359102520Sdarrenr				ackoff = aps->aps_seqoff[!sel2];
1360102520Sdarrenr		}
136195563Sdarrenr	}
1362255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1363145522Sdarrenr		printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1364145522Sdarrenr		       rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1365145522Sdarrenr		       thack, ackoff, mlen, fin->fin_plen, off);
1366145522Sdarrenr		printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1367145522Sdarrenr		       aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1368145522Sdarrenr		       aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1369145522Sdarrenr		printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1370145522Sdarrenr		       aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1371145522Sdarrenr		       aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1372145522Sdarrenr	}
1373102520Sdarrenr
137460857Sdarrenr	/*
137560857Sdarrenr	 * XXX - Ideally, this packet should get dropped because we now know
137660857Sdarrenr	 * that it is out of order (and there is no real danger in doing so
137760857Sdarrenr	 * apart from causing packets to go through here ordered).
137860857Sdarrenr	 */
1379255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1380145522Sdarrenr		printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1381145522Sdarrenr		       rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1382145522Sdarrenr	}
1383102520Sdarrenr
1384102520Sdarrenr	ok = 0;
1385110916Sdarrenr	if (t->ftps_seq[0] == 0) {
1386110916Sdarrenr		t->ftps_seq[0] = thack;
1387110916Sdarrenr		ok = 1;
1388110916Sdarrenr	} else {
1389102520Sdarrenr		if (ackoff == 0) {
1390102520Sdarrenr			if (t->ftps_seq[0] == thack)
1391102520Sdarrenr				ok = 1;
1392102520Sdarrenr			else if (t->ftps_seq[1] == thack) {
1393102520Sdarrenr				t->ftps_seq[0] = thack;
1394102520Sdarrenr				ok = 1;
1395102520Sdarrenr			}
1396102520Sdarrenr		} else {
1397255332Scy			if (t->ftps_seq[0] + ackoff == thack) {
1398255332Scy				t->ftps_seq[0] = thack;
1399102520Sdarrenr				ok = 1;
1400255332Scy			} else if (t->ftps_seq[0] == thack + ackoff) {
1401255332Scy				t->ftps_seq[0] = thack + ackoff;
1402102520Sdarrenr				ok = 1;
1403255332Scy			} else if (t->ftps_seq[1] + ackoff == thack) {
1404255332Scy				t->ftps_seq[0] = thack;
1405102520Sdarrenr				ok = 1;
1406102520Sdarrenr			} else if (t->ftps_seq[1] == thack + ackoff) {
1407255332Scy				t->ftps_seq[0] = thack + ackoff;
1408102520Sdarrenr				ok = 1;
1409102520Sdarrenr			}
1410102520Sdarrenr		}
1411102520Sdarrenr	}
1412102520Sdarrenr
1413255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1414145522Sdarrenr		if (!ok)
1415145522Sdarrenr			printf("%s ok\n", "not");
1416145522Sdarrenr	}
1417102520Sdarrenr
1418102520Sdarrenr	if (!mlen) {
1419255332Scy		if (t->ftps_seq[0] + ackoff != thack &&
1420255332Scy		    t->ftps_seq[1] + ackoff != thack) {
1421255332Scy			DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack);
1422255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1423255332Scy				printf("%s:seq[0](%u) + (%d) != (%u)\n",
1424255332Scy				       "ipf_p_ftp_process", t->ftps_seq[0],
1425145522Sdarrenr				       ackoff, thack);
1426255332Scy				printf("%s:seq[0](%u) + (%d) != (%u)\n",
1427255332Scy				       "ipf_p_ftp_process", t->ftps_seq[1],
1428255332Scy				       ackoff, thack);
1429145522Sdarrenr			}
143095563Sdarrenr			return APR_ERR(1);
1431110916Sdarrenr		}
1432102520Sdarrenr
1433255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE) {
1434255332Scy			printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n",
1435145522Sdarrenr				f->ftps_seq[0], f->ftps_seq[1]);
1436145522Sdarrenr		}
1437145522Sdarrenr
1438102520Sdarrenr		if (tcp->th_flags & TH_FIN) {
1439110916Sdarrenr			if (thseq == f->ftps_seq[1]) {
1440110916Sdarrenr				f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1441110916Sdarrenr				f->ftps_seq[1] = thseq + 1 - seqoff;
1442110916Sdarrenr			} else {
1443255332Scy				DT2(thseq, ftpside_t *t, t, u_32_t, thseq);
1444255332Scy				if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1445255332Scy					printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1446255332Scy					       thseq, seqoff, f->ftps_seq[0]);
1447145522Sdarrenr				}
1448102520Sdarrenr				return APR_ERR(1);
1449102520Sdarrenr			}
145095563Sdarrenr		}
1451102520Sdarrenr		f->ftps_len = 0;
1452102520Sdarrenr		return 0;
145360857Sdarrenr	}
1454102520Sdarrenr
1455102520Sdarrenr	ok = 0;
1456110916Sdarrenr	if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1457102520Sdarrenr		ok = 1;
1458102520Sdarrenr	/*
1459102520Sdarrenr	 * Retransmitted data packet.
1460102520Sdarrenr	 */
1461110916Sdarrenr	} else if ((thseq + mlen == f->ftps_seq[0]) ||
1462110916Sdarrenr		   (thseq + mlen == f->ftps_seq[1])) {
1463102520Sdarrenr		ok = 1;
1464110916Sdarrenr	}
1465110916Sdarrenr
1466102520Sdarrenr	if (ok == 0) {
1467255332Scy		DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen);
1468102520Sdarrenr		inc = thseq - f->ftps_seq[0];
1469255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1470145522Sdarrenr			printf("inc %d sel %d rv %d\n", inc, sel, rv);
1471145522Sdarrenr			printf("th_seq %x ftps_seq %x/%x\n",
1472145522Sdarrenr			       thseq, f->ftps_seq[0], f->ftps_seq[1]);
1473145522Sdarrenr			printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1474145522Sdarrenr			       aps->aps_ackoff[sel]);
1475145522Sdarrenr			printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1476145522Sdarrenr			       aps->aps_seqoff[sel]);
1477145522Sdarrenr		}
1478102520Sdarrenr
1479102520Sdarrenr		return APR_ERR(1);
1480102520Sdarrenr	}
1481102520Sdarrenr
148295563Sdarrenr	inc = 0;
1483102520Sdarrenr	rptr = f->ftps_rptr;
1484102520Sdarrenr	wptr = f->ftps_wptr;
1485102520Sdarrenr	f->ftps_seq[0] = thseq;
1486102520Sdarrenr	f->ftps_seq[1] = f->ftps_seq[0] + mlen;
148772006Sdarrenr	f->ftps_len = mlen;
148860857Sdarrenr
148960857Sdarrenr	while (mlen > 0) {
1490145522Sdarrenr		len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1491255332Scy		if (len == 0)
1492255332Scy			break;
1493145522Sdarrenr		COPYDATA(m, off, len, wptr);
149460857Sdarrenr		mlen -= len;
149560857Sdarrenr		off += len;
149660857Sdarrenr		wptr += len;
1497145522Sdarrenr
1498255332Scywhilemore:
1499255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1500145522Sdarrenr			printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
1501255332Scy			       "ipf_p_ftp_process",
1502145522Sdarrenr			       len, mlen, off, (u_long)wptr, f->ftps_junk,
1503145522Sdarrenr			       len, len, rptr);
1504145522Sdarrenr
150560857Sdarrenr		f->ftps_wptr = wptr;
1506255332Scy		if (f->ftps_junk != FTPXY_JUNK_OK) {
1507145522Sdarrenr			i = f->ftps_junk;
1508255332Scy			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr,
1509110916Sdarrenr						      wptr - rptr);
1510255332Scy			DT2(junk_transit, int, i, int, f->ftps_junk);
151160857Sdarrenr
1512255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1513145522Sdarrenr				printf("%s:junk %d -> %d\n",
1514255332Scy				       "ipf_p_ftp_process", i, f->ftps_junk);
1515145522Sdarrenr
1516255332Scy			if (f->ftps_junk == FTPXY_JUNK_BAD) {
1517255332Scy				DT(buffer_full);
1518145522Sdarrenr				if (wptr - rptr == sizeof(f->ftps_buf)) {
1519255332Scy					if (softf->ipf_p_ftp_debug &
1520255332Scy					    DEBUG_PARSE_INFO)
1521145522Sdarrenr						printf("%s:full buffer\n",
1522255332Scy						       "ipf_p_ftp_process");
1523145522Sdarrenr					f->ftps_rptr = f->ftps_buf;
1524145522Sdarrenr					f->ftps_wptr = f->ftps_buf;
1525145522Sdarrenr					rptr = f->ftps_rptr;
1526145522Sdarrenr					wptr = f->ftps_wptr;
1527145522Sdarrenr					continue;
1528145522Sdarrenr				}
1529145522Sdarrenr			}
1530145522Sdarrenr		}
1531145522Sdarrenr
1532255332Scy		while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) {
1533145522Sdarrenr			len = wptr - rptr;
1534255332Scy			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv,
1535255332Scy						       rptr, len);
1536145522Sdarrenr
1537255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_PARSE) {
1538145522Sdarrenr				printf("%s=%d len %d rv %d ptr %lx/%lx ",
1539255332Scy				       "ipf_p_ftp_valid",
1540145522Sdarrenr				       f->ftps_junk, len, rv, (u_long)rptr,
1541145522Sdarrenr				       (u_long)wptr);
1542145522Sdarrenr				printf("buf [%*.*s]\n", len, len, rptr);
1543145522Sdarrenr			}
1544145522Sdarrenr
1545255332Scy			if (f->ftps_junk == FTPXY_JUNK_OK) {
1546255332Scy				f->ftps_cmds++;
154760857Sdarrenr				f->ftps_rptr = rptr;
154860857Sdarrenr				if (rv)
1549255332Scy					inc += ipf_p_ftp_server(softf, fin, ip,
1550255332Scy								nat, ftp, len);
155160857Sdarrenr				else
1552255332Scy					inc += ipf_p_ftp_client(softf, fin, ip,
1553255332Scy								nat, ftp, len);
155460857Sdarrenr				rptr = f->ftps_rptr;
155592685Sdarrenr				wptr = f->ftps_wptr;
155660857Sdarrenr			}
155760857Sdarrenr		}
155860857Sdarrenr
155992685Sdarrenr		/*
156092685Sdarrenr		 * Off to a bad start so lets just forget about using the
156192685Sdarrenr		 * ftp proxy for this connection.
156292685Sdarrenr		 */
1563255332Scy		if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) {
1564102520Sdarrenr			/* f->ftps_seq[1] += inc; */
1565145522Sdarrenr
1566255332Scy			DT(ftp_junk_cmd);
1567255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1568145522Sdarrenr				printf("%s:cmds == 0 junk == 1\n",
1569255332Scy				       "ipf_p_ftp_process");
157092685Sdarrenr			return APR_ERR(2);
157195563Sdarrenr		}
157292685Sdarrenr
1573255332Scy		retry = 0;
1574255332Scy		if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) {
1575145522Sdarrenr			for (s = rptr; s < wptr; s++) {
1576145522Sdarrenr				if ((*s == '\r') && (s + 1 < wptr) &&
1577145522Sdarrenr				    (*(s + 1) == '\n')) {
1578145522Sdarrenr					rptr = s + 2;
1579255332Scy					retry = 1;
1580255332Scy					if (f->ftps_junk != FTPXY_JUNK_CONT)
1581255332Scy						f->ftps_junk = FTPXY_JUNK_OK;
158267614Sdarrenr					break;
1583145522Sdarrenr				}
158460857Sdarrenr			}
158560857Sdarrenr		}
158660857Sdarrenr
158760857Sdarrenr		if (rptr == wptr) {
158860857Sdarrenr			rptr = wptr = f->ftps_buf;
158960857Sdarrenr		} else {
1590145522Sdarrenr			/*
1591145522Sdarrenr			 * Compact the buffer back to the start.  The junk
1592145522Sdarrenr			 * flag should already be set and because we're not
1593145522Sdarrenr			 * throwing away any data, it is preserved from its
1594145522Sdarrenr			 * current state.
1595145522Sdarrenr			 */
1596145522Sdarrenr			if (rptr > f->ftps_buf) {
1597255332Scy				bcopy(rptr, f->ftps_buf, wptr - rptr);
1598145522Sdarrenr				wptr -= rptr - f->ftps_buf;
1599145522Sdarrenr				rptr = f->ftps_buf;
160060857Sdarrenr			}
160160857Sdarrenr		}
1602145522Sdarrenr		f->ftps_rptr = rptr;
1603145522Sdarrenr		f->ftps_wptr = wptr;
1604255332Scy		if (retry)
1605255332Scy			goto whilemore;
160660857Sdarrenr	}
160760857Sdarrenr
1608102520Sdarrenr	/* f->ftps_seq[1] += inc; */
1609102520Sdarrenr	if (tcp->th_flags & TH_FIN)
1610102520Sdarrenr		f->ftps_seq[1]++;
1611255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) {
1612145522Sdarrenr#ifdef __sgi
1613145522Sdarrenr		mlen = fin->fin_plen;
1614145522Sdarrenr#else
1615145522Sdarrenr		mlen = MSGDSIZE(m);
1616102520Sdarrenr#endif
1617145522Sdarrenr		mlen -= off;
1618145522Sdarrenr		printf("ftps_seq[1] = %x inc %d len %d\n",
1619145522Sdarrenr		       f->ftps_seq[1], inc, mlen);
1620145522Sdarrenr	}
1621102520Sdarrenr
162260857Sdarrenr	f->ftps_rptr = rptr;
162360857Sdarrenr	f->ftps_wptr = wptr;
162464580Sdarrenr	return APR_INC(inc);
162560857Sdarrenr}
162660857Sdarrenr
162760857Sdarrenr
1628255332Scyint
1629255332Scyipf_p_ftp_out(arg, fin, aps, nat)
1630255332Scy	void *arg;
1631255332Scy	fr_info_t *fin;
1632255332Scy	ap_session_t *aps;
1633255332Scy	nat_t *nat;
163460857Sdarrenr{
1635255332Scy	ipf_ftp_softc_t *softf = arg;
163660857Sdarrenr	ftpinfo_t *ftp;
1637145522Sdarrenr	int rev;
163860857Sdarrenr
163960857Sdarrenr	ftp = aps->aps_data;
164060857Sdarrenr	if (ftp == NULL)
164160857Sdarrenr		return 0;
1642145522Sdarrenr
1643145522Sdarrenr	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1644145522Sdarrenr	if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1645145522Sdarrenr		ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1646145522Sdarrenr
1647255332Scy	return ipf_p_ftp_process(softf, fin, nat, ftp, rev);
164860857Sdarrenr}
164960857Sdarrenr
165060857Sdarrenr
1651255332Scyint
1652255332Scyipf_p_ftp_in(arg, fin, aps, nat)
1653255332Scy	void *arg;
1654255332Scy	fr_info_t *fin;
1655255332Scy	ap_session_t *aps;
1656255332Scy	nat_t *nat;
165753642Sguido{
1658255332Scy	ipf_ftp_softc_t *softf = arg;
165960857Sdarrenr	ftpinfo_t *ftp;
1660145522Sdarrenr	int rev;
166153642Sguido
166260857Sdarrenr	ftp = aps->aps_data;
166360857Sdarrenr	if (ftp == NULL)
166460857Sdarrenr		return 0;
1665145522Sdarrenr
1666145522Sdarrenr	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1667145522Sdarrenr	if (ftp->ftp_side[rev].ftps_ifp == NULL)
1668145522Sdarrenr		ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1669145522Sdarrenr
1670255332Scy	return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev);
167153642Sguido}
167260857Sdarrenr
167360857Sdarrenr
167460857Sdarrenr/*
1675255332Scy * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in
167660857Sdarrenr * pairs separated by commas (which are expected to be in the range 0 - 255),
167760857Sdarrenr * returning a 16 bit number combining either side of the , as the MSB and
167860857Sdarrenr * LSB.
167960857Sdarrenr */
1680255332Scyu_short
1681255332Scyipf_p_ftp_atoi(ptr)
1682255332Scy	char **ptr;
168360857Sdarrenr{
168460857Sdarrenr	register char *s = *ptr, c;
168560857Sdarrenr	register u_char i = 0, j = 0;
168660857Sdarrenr
1687145522Sdarrenr	while (((c = *s++) != '\0') && ISDIGIT(c)) {
168860857Sdarrenr		i *= 10;
168960857Sdarrenr		i += c - '0';
169060857Sdarrenr	}
169160857Sdarrenr	if (c != ',') {
169260857Sdarrenr		*ptr = NULL;
169360857Sdarrenr		return 0;
169460857Sdarrenr	}
1695145522Sdarrenr	while (((c = *s++) != '\0') && ISDIGIT(c)) {
169660857Sdarrenr		j *= 10;
169760857Sdarrenr		j += c - '0';
169860857Sdarrenr	}
169960857Sdarrenr	*ptr = s;
170067614Sdarrenr	i &= 0xff;
170167614Sdarrenr	j &= 0xff;
170260857Sdarrenr	return (i << 8) | j;
170360857Sdarrenr}
1704145522Sdarrenr
1705145522Sdarrenr
1706255332Scyint
1707255332Scyipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen)
1708255332Scy	ipf_ftp_softc_t *softf;
1709255332Scy	fr_info_t *fin;
1710255332Scy	ip_t *ip;
1711255332Scy	nat_t *nat;
1712255332Scy	ftpinfo_t *ftp;
1713255332Scy	int dlen;
1714145522Sdarrenr{
1715255332Scy	ftpside_t *f;
1716255332Scy
1717255332Scy	/*
1718255332Scy	 * Check for client sending out EPRT message.
1719255332Scy	 */
1720255332Scy	if (dlen < IPF_MINEPRTLEN) {
1721255332Scy		DT1(epert_dlen, int, dlen);
1722255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1723255332Scy			printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n",
1724255332Scy				dlen);
1725255332Scy		return 0;
1726255332Scy	}
1727255332Scy
1728255332Scy	/*
1729255332Scy	 * Parse the EPRT command.  Format is:
1730255332Scy	 * "EPRT |1|1.2.3.4|2000|" for IPv4 and
1731255332Scy	 * "EPRT |2|ef00::1:2|2000|" for IPv6
1732255332Scy	 */
1733255332Scy	f = &ftp->ftp_side[0];
1734255332Scy	if (f->ftps_rptr[5] != '|')
1735255332Scy		return 0;
1736255332Scy	if (f->ftps_rptr[5] == f->ftps_rptr[7]) {
1737255332Scy		if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4)
1738255332Scy			return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen);
1739255332Scy#ifdef USE_INET6
1740255332Scy		if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6)
1741255332Scy			return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen);
1742255332Scy#endif
1743255332Scy	}
1744255332Scy	return 0;
1745255332Scy}
1746255332Scy
1747255332Scy
1748255332Scyint
1749255332Scyipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen)
1750255332Scy	ipf_ftp_softc_t *softf;
1751255332Scy	fr_info_t *fin;
1752255332Scy	ip_t *ip;
1753255332Scy	nat_t *nat;
1754255332Scy	ftpinfo_t *ftp;
1755255332Scy	int dlen;
1756255332Scy{
1757255332Scy	int a1, a2, a3, a4, port, olen, nlen, inc, off;
1758145522Sdarrenr	char newbuf[IPF_FTPBUFSZ];
1759255332Scy	char *s, c, delim;
1760255332Scy	u_32_t addr, i;
1761255332Scy	tcphdr_t *tcp;
1762255332Scy	ftpside_t *f;
1763255332Scy	mb_t *m;
1764255332Scy
1765255332Scy	m = fin->fin_m;
1766255332Scy	tcp = (tcphdr_t *)fin->fin_dp;
1767255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1768255332Scy	f = &ftp->ftp_side[0];
1769255332Scy	delim = f->ftps_rptr[5];
1770255332Scy	s = f->ftps_rptr + 8;
1771255332Scy
1772255332Scy	/*
1773255332Scy	 * get the IP address.
1774255332Scy	 */
1775255332Scy	i = 0;
1776255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1777255332Scy		i *= 10;
1778255332Scy		i += c - '0';
1779255332Scy	}
1780255332Scy	if (i > 255)
1781255332Scy		return 0;
1782255332Scy	if (c != '.')
1783255332Scy		return 0;
1784255332Scy	addr = (i << 24);
1785255332Scy
1786255332Scy	i = 0;
1787255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1788255332Scy		i *= 10;
1789255332Scy		i += c - '0';
1790255332Scy	}
1791255332Scy	if (i > 255)
1792255332Scy		return 0;
1793255332Scy	if (c != '.')
1794255332Scy		return 0;
1795255332Scy	addr |= (addr << 16);
1796255332Scy
1797255332Scy	i = 0;
1798255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1799255332Scy		i *= 10;
1800255332Scy		i += c - '0';
1801255332Scy	}
1802255332Scy	if (i > 255)
1803255332Scy		return 0;
1804255332Scy	if (c != '.')
1805255332Scy		return 0;
1806255332Scy	addr |= (addr << 8);
1807255332Scy
1808255332Scy	i = 0;
1809255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1810255332Scy		i *= 10;
1811255332Scy		i += c - '0';
1812255332Scy	}
1813255332Scy	if (i > 255)
1814255332Scy		return 0;
1815255332Scy	if (c != delim)
1816255332Scy		return 0;
1817255332Scy	addr |= addr;
1818255332Scy
1819255332Scy	/*
1820255332Scy	 * Get the port number
1821255332Scy	 */
1822255332Scy	i = 0;
1823255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1824255332Scy		i *= 10;
1825255332Scy		i += c - '0';
1826255332Scy	}
1827255332Scy	if (i > 65535)
1828255332Scy		return 0;
1829255332Scy	if (c != delim)
1830255332Scy		return 0;
1831255332Scy	port = i;
1832255332Scy
1833255332Scy	/*
1834255332Scy	 * Check for CR-LF at the end of the command string.
1835255332Scy	 */
1836255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
1837255332Scy		DT(eprt4_no_crlf);
1838255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1839255332Scy			printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf");
1840255332Scy		return 0;
1841255332Scy	}
1842255332Scy	s += 2;
1843255332Scy
1844255332Scy	/*
1845255332Scy	 * Calculate new address parts for PORT command
1846255332Scy	 */
1847255332Scy	if (nat->nat_dir == NAT_INBOUND)
1848255332Scy		a1 = ntohl(nat->nat_odstaddr);
1849255332Scy	else
1850255332Scy		a1 = ntohl(ip->ip_src.s_addr);
1851255332Scy	a2 = (a1 >> 16) & 0xff;
1852255332Scy	a3 = (a1 >> 8) & 0xff;
1853255332Scy	a4 = a1 & 0xff;
1854255332Scy	a1 >>= 24;
1855255332Scy	olen = s - f->ftps_rptr;
1856255332Scy	/* DO NOT change this to snprintf! */
1857255332Scy	/*
1858255332Scy	 * While we could force the use of | as a delimiter here, it makes
1859255332Scy	 * sense to preserve whatever character is being used by the systems
1860255332Scy	 * involved in the communication.
1861255332Scy	 */
1862255332Scy#if defined(SNPRINTF) && defined(_KERNEL)
1863255332Scy	SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
1864255332Scy		 "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim);
1865255332Scy#else
1866255332Scy	(void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
1867255332Scy		       "EPRT", delim, delim, a1, a2, a3, a4, delim, port,
1868255332Scy			delim);
1869255332Scy#endif
1870255332Scy
1871255332Scy	nlen = strlen(newbuf);
1872255332Scy	inc = nlen - olen;
1873255332Scy	if ((inc + fin->fin_plen) > 65535) {
1874255332Scy		DT2(eprt4_len, int, inc, int, fin->fin_plen);
1875255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1876255332Scy			printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n",
1877255332Scy				inc);
1878255332Scy		return 0;
1879255332Scy	}
1880255332Scy
1881255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1882255332Scy#if !defined(_KERNEL)
1883255332Scy	M_ADJ(m, inc);
1884255332Scy#else
1885255332Scy	if (inc < 0)
1886255332Scy		M_ADJ(m, inc);
1887255332Scy#endif
1888255332Scy	/* the mbuf chain will be extended if necessary by m_copyback() */
1889255332Scy	COPYBACK(m, off, nlen, newbuf);
1890255332Scy	fin->fin_flx |= FI_DOCKSUM;
1891255332Scy
1892255332Scy	if (inc != 0) {
1893255332Scy		fin->fin_plen += inc;
1894255332Scy		ip->ip_len = htons(fin->fin_plen);
1895255332Scy		fin->fin_dlen += inc;
1896255332Scy	}
1897255332Scy
1898255332Scy	f->ftps_cmd = FTPXY_C_EPRT;
1899255332Scy	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
1900255332Scy}
1901255332Scy
1902255332Scy
1903255332Scyint
1904255332Scyipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen)
1905255332Scy	ipf_ftp_softc_t *softf;
1906255332Scy	fr_info_t *fin;
1907255332Scy	ip_t *ip;
1908255332Scy	nat_t *nat;
1909255332Scy	ftpinfo_t *ftp;
1910255332Scy	int dlen;
1911255332Scy{
1912255332Scy	char newbuf[IPF_FTPBUFSZ];
1913255332Scy	u_short ap = 0;
1914255332Scy	ftpside_t *f;
1915145522Sdarrenr	char *s;
1916145522Sdarrenr
1917255332Scy	if ((softf->ipf_p_ftp_forcepasv != 0) &&
1918255332Scy	    (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) {
1919255332Scy		DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd);
1920255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1921255332Scy			printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n",
1922255332Scy			       ftp->ftp_side[0].ftps_cmd);
1923255332Scy		return 0;
1924255332Scy	}
1925255332Scy	f = &ftp->ftp_side[1];
1926255332Scy
1927145522Sdarrenr#define EPSV_REPLEN	33
1928145522Sdarrenr	/*
1929145522Sdarrenr	 * Check for EPSV reply message.
1930145522Sdarrenr	 */
1931255332Scy	if (dlen < IPF_MIN229LEN) {
1932145522Sdarrenr		return (0);
1933255332Scy	} else if (strncmp(f->ftps_rptr,
1934255332Scy			 "229 Entering Extended Passive Mode", EPSV_REPLEN)) {
1935145522Sdarrenr		return (0);
1936255332Scy}
1937145522Sdarrenr
1938145522Sdarrenr	/*
1939145522Sdarrenr	 * Skip the EPSV command + space
1940145522Sdarrenr	 */
1941145522Sdarrenr	s = f->ftps_rptr + 33;
1942145522Sdarrenr	while (*s && !ISDIGIT(*s))
1943145522Sdarrenr		s++;
1944145522Sdarrenr
1945145522Sdarrenr	/*
1946145522Sdarrenr	 * As per RFC 2428, there are no addres components in the EPSV
1947145522Sdarrenr	 * response.  So we'll go straight to getting the port.
1948145522Sdarrenr	 */
1949145522Sdarrenr	while (*s && ISDIGIT(*s)) {
1950145522Sdarrenr		ap *= 10;
1951145522Sdarrenr		ap += *s++ - '0';
1952145522Sdarrenr	}
1953145522Sdarrenr
1954145522Sdarrenr	if (*s == '|')
1955145522Sdarrenr		s++;
1956145522Sdarrenr	if (*s == ')')
1957145522Sdarrenr		s++;
1958145522Sdarrenr	if (*s == '\n')
1959145522Sdarrenr		s--;
1960145522Sdarrenr	/*
1961145522Sdarrenr	 * check for CR-LF at the end.
1962145522Sdarrenr	 */
1963255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
1964145522Sdarrenr		return 0;
1965255332Scy	}
1966255332Scy	s += 2;
1967145522Sdarrenr
1968145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL)
1969145522Sdarrenr	SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1970145522Sdarrenr		 "229 Entering Extended Passive Mode", ap);
1971145522Sdarrenr#else
1972145522Sdarrenr	(void) sprintf(newbuf, "%s (|||%u|)\r\n",
1973145522Sdarrenr		       "229 Entering Extended Passive Mode", ap);
1974145522Sdarrenr#endif
1975145522Sdarrenr
1976255332Scy	return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap,
1977255332Scy				   newbuf, s);
1978145522Sdarrenr}
1979255332Scy
1980255332Scy#ifdef USE_INET6
1981255332Scyint
1982255332Scyipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen)
1983255332Scy	ipf_ftp_softc_t *softf;
1984255332Scy	fr_info_t *fin;
1985255332Scy	ip_t *ip;
1986255332Scy	nat_t *nat;
1987255332Scy	ftpinfo_t *ftp;
1988255332Scy	int dlen;
1989255332Scy{
1990255332Scy	int port, olen, nlen, inc, off, left, i;
1991255332Scy	char newbuf[IPF_FTPBUFSZ];
1992255332Scy	char *s, c;
1993255332Scy	i6addr_t addr, *a6;
1994255332Scy	tcphdr_t *tcp;
1995255332Scy	ip6_t *ip6;
1996255332Scy	char delim;
1997255332Scy	u_short whole;
1998255332Scy	u_short part;
1999255332Scy	ftpside_t *f;
2000255332Scy	u_short *t;
2001255332Scy	int fwd;
2002255332Scy	mb_t *m;
2003255332Scy	u_32_t a;
2004255332Scy
2005255332Scy	m = fin->fin_m;
2006255332Scy	ip6 = (ip6_t *)ip;
2007255332Scy	f = &ftp->ftp_side[0];
2008255332Scy	s = f->ftps_rptr + 8;
2009255332Scy	f = &ftp->ftp_side[0];
2010255332Scy	delim = f->ftps_rptr[5];
2011255332Scy	tcp = (tcphdr_t *)fin->fin_dp;
2012255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
2013255332Scy
2014255332Scy	addr.i6[0] = 0;
2015255332Scy	addr.i6[1] = 0;
2016255332Scy	addr.i6[2] = 0;
2017255332Scy	addr.i6[3] = 0;
2018255332Scy	/*
2019255332Scy	 * Parse an IPv6 address.
2020255332Scy	 * Go forward until either :: or | is found. If :: is found,
2021255332Scy	 * reverse direction. Direction change is performed to ease
2022255332Scy	 * parsing an unknown number of 0s in the middle.
2023255332Scy	 */
2024255332Scy	whole = 0;
2025255332Scy	t = (u_short *)&addr;
2026255332Scy	fwd = 1;
2027255332Scy	for (part = 0; (c = *s) != '\0'; ) {
2028255332Scy		if (c == delim) {
2029255332Scy			*t = htons((u_short)whole);
2030255332Scy			break;
2031255332Scy		}
2032255332Scy		if (c == ':') {
2033255332Scy			*t = part;
2034255332Scy			if (fwd) {
2035255332Scy				*t = htons((u_short)whole);
2036255332Scy				t++;
2037255332Scy			} else {
2038255332Scy				*t = htons((u_short)(whole >> 16));
2039255332Scy				t--;
2040255332Scy			}
2041255332Scy			whole = 0;
2042255332Scy			if (fwd == 1 && s[1] == ':') {
2043255332Scy				while (*s && *s != '|')
2044255332Scy					s++;
2045255332Scy				if ((c = *s) != delim)
2046255332Scy					break;
2047255332Scy				t = (u_short *)&addr.i6[3];
2048255332Scy				t++;
2049255332Scy				fwd = 0;
2050255332Scy			} else if (fwd == 0 && s[-1] == ':') {
2051255332Scy				break;
2052255332Scy			}
2053255332Scy		} else {
2054255332Scy			if (c >= '0' && c <= '9') {
2055255332Scy				c -= '0';
2056255332Scy			} else if (c >= 'a' && c <= 'f') {
2057255332Scy				c -= 'a' + 10;
2058255332Scy			} else if (c >= 'A' && c <= 'F') {
2059255332Scy				c -= 'A' + 10;
2060255332Scy			}
2061255332Scy			if (fwd) {
2062255332Scy				whole <<= 8;
2063255332Scy				whole |= c;
2064255332Scy			} else {
2065255332Scy				whole >>= 8;
2066255332Scy				whole |= ((u_32_t)c) << 24;
2067255332Scy			}
2068255332Scy		}
2069255332Scy		if (fwd)
2070255332Scy			s++;
2071255332Scy		else
2072255332Scy			s--;
2073255332Scy	}
2074255332Scy	if (c != ':' && c != delim)
2075255332Scy		return 0;
2076255332Scy
2077255332Scy	while (*s != '|')
2078255332Scy		s++;
2079255332Scy	s++;
2080255332Scy
2081255332Scy	/*
2082255332Scy	 * Get the port number
2083255332Scy	 */
2084255332Scy	i = 0;
2085255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
2086255332Scy		i *= 10;
2087255332Scy		i += c - '0';
2088255332Scy	}
2089255332Scy	if (i > 65535)
2090255332Scy		return 0;
2091255332Scy	if (c != delim)
2092255332Scy		return 0;
2093255332Scy	port = (u_short)(i & 0xffff);
2094255332Scy
2095255332Scy	/*
2096255332Scy	 * Check for CR-LF at the end of the command string.
2097255332Scy	 */
2098255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
2099255332Scy		DT(eprt6_no_crlf);
2100255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
2101255332Scy			printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf");
2102255332Scy		return 0;
2103255332Scy	}
2104255332Scy	s += 2;
2105255332Scy
2106255332Scy	/*
2107255332Scy	 * Calculate new address parts for PORT command
2108255332Scy	 */
2109255332Scy	a6 = (i6addr_t *)&ip6->ip6_src;
2110255332Scy	olen = s - f->ftps_rptr;
2111255332Scy	/* DO NOT change this to snprintf! */
2112255332Scy	/*
2113255332Scy	 * While we could force the use of | as a delimiter here, it makes
2114255332Scy	 * sense to preserve whatever character is being used by the systems
2115255332Scy	 * involved in the communication.
2116255332Scy	 */
2117255332Scy	s = newbuf;
2118255332Scy	left = sizeof(newbuf);
2119255332Scy#if defined(SNPRINTF) && defined(_KERNEL)
2120255332Scy	SNPRINTF(newbuf, left, "EPRT %c2%c", delim, delim);
2121255332Scy	left -= strlen(s) + 1;
2122255332Scy	s += strlen(s);
2123255332Scy	a = ntohl(a6->i6[0]);
2124255332Scy	SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff);
2125255332Scy	left -= strlen(s);
2126255332Scy	s += strlen(s);
2127255332Scy	a = ntohl(a6->i6[1]);
2128255332Scy	SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff);
2129255332Scy	left -= strlen(s);
2130255332Scy	s += strlen(s);
2131255332Scy	a = ntohl(a6->i6[2]);
2132255332Scy	SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff);
2133255332Scy	left -= strlen(s);
2134255332Scy	s += strlen(s);
2135255332Scy	a = ntohl(a6->i6[3]);
2136255332Scy	SNPRINTF(s, left, "%x:%x", a >> 16, a & 0xffff);
2137255332Scy	left -= strlen(s);
2138255332Scy	s += strlen(s);
2139328203Scy	SNPRINTF(s, left, "|%d|\r\n", port);
2140255332Scy#else
2141255332Scy	(void) sprintf(s, "EPRT %c2%c", delim, delim);
2142255332Scy	s += strlen(s);
2143255332Scy	a = ntohl(a6->i6[0]);
2144255332Scy	sprintf(s, "%x:%x:", a >> 16, a & 0xffff);
2145255332Scy	s += strlen(s);
2146255332Scy	a = ntohl(a6->i6[1]);
2147255332Scy	sprintf(s, "%x:%x:", a >> 16, a & 0xffff);
2148255332Scy	left -= strlen(s);
2149255332Scy	s += strlen(s);
2150255332Scy	a = ntohl(a6->i6[2]);
2151255332Scy	sprintf(s, "%x:%x:", a >> 16, a & 0xffff);
2152255332Scy	left -= strlen(s);
2153255332Scy	s += strlen(s);
2154255332Scy	a = ntohl(a6->i6[3]);
2155255332Scy	sprintf(s, "%x:%x", a >> 16, a & 0xffff);
2156255332Scy	left -= strlen(s);
2157255332Scy	s += strlen(s);
2158255332Scy	sprintf(s, "|%d|\r\n", port);
2159255332Scy#endif
2160255332Scy	nlen = strlen(newbuf);
2161255332Scy	inc = nlen - olen;
2162255332Scy	if ((inc + fin->fin_plen) > 65535) {
2163255332Scy		DT2(eprt6_len, int, inc, int, fin->fin_plen);
2164255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
2165255332Scy			printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n",
2166255332Scy				inc);
2167255332Scy		return 0;
2168255332Scy	}
2169255332Scy
2170255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
2171255332Scy#if !defined(_KERNEL)
2172255332Scy	M_ADJ(m, inc);
2173255332Scy#else
2174255332Scy	if (inc < 0)
2175255332Scy		M_ADJ(m, inc);
2176255332Scy#endif
2177255332Scy	/* the mbuf chain will be extended if necessary by m_copyback() */
2178255332Scy	COPYBACK(m, off, nlen, newbuf);
2179255332Scy	fin->fin_flx |= FI_DOCKSUM;
2180255332Scy
2181255332Scy	if (inc != 0) {
2182255332Scy		fin->fin_plen += inc;
2183255332Scy		ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen);
2184255332Scy		fin->fin_dlen += inc;
2185255332Scy	}
2186255332Scy
2187255332Scy	f->ftps_cmd = FTPXY_C_EPRT;
2188255332Scy	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
2189255332Scy}
2190255332Scy#endif
2191