1145522Sdarrenr/*	$FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 369245 2021-02-09 13:47:46Z git2svn $	*/
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/11/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 369245 2021-02-09 13:47:46Z git2svn $
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
69369245Sgit2svnvoid ipf_p_ftp_main_load(void);
70369245Sgit2svnvoid ipf_p_ftp_main_unload(void);
71369245Sgit2svnvoid *ipf_p_ftp_soft_create(ipf_main_softc_t *);
72369245Sgit2svnvoid ipf_p_ftp_soft_destroy(ipf_main_softc_t *, void *);
73255332Scy
74369245Sgit2svnint ipf_p_ftp_client(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
75369245Sgit2svn			  ftpinfo_t *, int);
76369245Sgit2svnint ipf_p_ftp_complete(char *, size_t);
77369245Sgit2svnint ipf_p_ftp_in(void *, fr_info_t *, ap_session_t *, nat_t *);
78369245Sgit2svnint ipf_p_ftp_new(void *, fr_info_t *, ap_session_t *, nat_t *);
79369245Sgit2svnvoid ipf_p_ftp_del(ipf_main_softc_t *, ap_session_t *);
80369245Sgit2svnint ipf_p_ftp_out(void *, fr_info_t *, ap_session_t *, nat_t *);
81369245Sgit2svnint ipf_p_ftp_pasv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
82369245Sgit2svn			ftpinfo_t *, int);
83369245Sgit2svnint ipf_p_ftp_epsv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
84369245Sgit2svn			ftpinfo_t *, int);
85369245Sgit2svnint ipf_p_ftp_port(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
86369245Sgit2svn			ftpinfo_t *, int);
87369245Sgit2svnint ipf_p_ftp_process(ipf_ftp_softc_t *, fr_info_t *, nat_t *,
88369245Sgit2svn			   ftpinfo_t *, int);
89369245Sgit2svnint ipf_p_ftp_server(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
90369245Sgit2svn			  ftpinfo_t *, int);
91369245Sgit2svnint ipf_p_ftp_valid(ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t);
92369245Sgit2svnint ipf_p_ftp_server_valid(ipf_ftp_softc_t *, ftpside_t *, char *,
93369245Sgit2svn				size_t);
94369245Sgit2svnint ipf_p_ftp_client_valid(ipf_ftp_softc_t *, ftpside_t *, char *,
95369245Sgit2svn				size_t);
96369245Sgit2svnu_short ipf_p_ftp_atoi(char **);
97369245Sgit2svnint ipf_p_ftp_pasvreply(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
98369245Sgit2svn			     ftpinfo_t *, u_int, char *, char *);
99369245Sgit2svnint ipf_p_ftp_eprt(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
100369245Sgit2svn			ftpinfo_t *, int);
101369245Sgit2svnint ipf_p_ftp_eprt4(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
102369245Sgit2svn			 ftpinfo_t *, int);
103369245Sgit2svnint ipf_p_ftp_eprt6(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
104369245Sgit2svn			 ftpinfo_t *, int);
105369245Sgit2svnint ipf_p_ftp_addport(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
106369245Sgit2svn			   ftpinfo_t *, int, int, int);
107369245Sgit2svnvoid ipf_p_ftp_setpending(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	mlen = MSGDSIZE(m) - off;
131272006Sdarrenr
1313255332Scy	DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen);
1314255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1315255332Scy		printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n",
1316255332Scy		       fin->fin_out, fin->fin_sport, fin->fin_dport,
1317255332Scy		       mlen, tcp->th_flags);
1318255332Scy
1319161356Sguido	if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) {
1320161356Sguido		f->ftps_seq[0] = thseq + 1;
1321161356Sguido		t->ftps_seq[0] = thack;
1322145522Sdarrenr		return 0;
1323161356Sguido	} else if (mlen < 0) {
1324161356Sguido		return 0;
1325145522Sdarrenr	}
1326161356Sguido
1327102520Sdarrenr	aps = nat->nat_aps;
132860857Sdarrenr
1329102520Sdarrenr	sel = aps->aps_sel[1 - rv];
1330102520Sdarrenr	sel2 = aps->aps_sel[rv];
1331255332Scy	if (rv == 1) {
1332102520Sdarrenr		seqoff = aps->aps_seqoff[sel];
1333102520Sdarrenr		if (aps->aps_seqmin[sel] > seqoff + thseq)
1334102520Sdarrenr			seqoff = aps->aps_seqoff[!sel];
1335102520Sdarrenr		ackoff = aps->aps_ackoff[sel2];
1336102520Sdarrenr		if (aps->aps_ackmin[sel2] > ackoff + thack)
1337102520Sdarrenr			ackoff = aps->aps_ackoff[!sel2];
1338102520Sdarrenr	} else {
1339102520Sdarrenr		seqoff = aps->aps_ackoff[sel];
1340255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1341145522Sdarrenr			printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1342145522Sdarrenr			       aps->aps_ackmin[sel]);
1343102520Sdarrenr		if (aps->aps_ackmin[sel] > seqoff + thseq)
1344102520Sdarrenr			seqoff = aps->aps_ackoff[!sel];
134560857Sdarrenr
1346102520Sdarrenr		ackoff = aps->aps_seqoff[sel2];
1347255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1348145522Sdarrenr			printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1349145522Sdarrenr			       aps->aps_seqmin[sel2]);
1350102520Sdarrenr		if (ackoff > 0) {
1351102520Sdarrenr			if (aps->aps_seqmin[sel2] > ackoff + thack)
1352102520Sdarrenr				ackoff = aps->aps_seqoff[!sel2];
1353102520Sdarrenr		} else {
1354102520Sdarrenr			if (aps->aps_seqmin[sel2] > thack)
1355102520Sdarrenr				ackoff = aps->aps_seqoff[!sel2];
1356102520Sdarrenr		}
135795563Sdarrenr	}
1358255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1359145522Sdarrenr		printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1360145522Sdarrenr		       rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1361145522Sdarrenr		       thack, ackoff, mlen, fin->fin_plen, off);
1362145522Sdarrenr		printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1363145522Sdarrenr		       aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1364145522Sdarrenr		       aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1365145522Sdarrenr		printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1366145522Sdarrenr		       aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1367145522Sdarrenr		       aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1368145522Sdarrenr	}
1369102520Sdarrenr
137060857Sdarrenr	/*
137160857Sdarrenr	 * XXX - Ideally, this packet should get dropped because we now know
137260857Sdarrenr	 * that it is out of order (and there is no real danger in doing so
137360857Sdarrenr	 * apart from causing packets to go through here ordered).
137460857Sdarrenr	 */
1375255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1376145522Sdarrenr		printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1377145522Sdarrenr		       rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1378145522Sdarrenr	}
1379102520Sdarrenr
1380102520Sdarrenr	ok = 0;
1381110916Sdarrenr	if (t->ftps_seq[0] == 0) {
1382110916Sdarrenr		t->ftps_seq[0] = thack;
1383110916Sdarrenr		ok = 1;
1384110916Sdarrenr	} else {
1385102520Sdarrenr		if (ackoff == 0) {
1386102520Sdarrenr			if (t->ftps_seq[0] == thack)
1387102520Sdarrenr				ok = 1;
1388102520Sdarrenr			else if (t->ftps_seq[1] == thack) {
1389102520Sdarrenr				t->ftps_seq[0] = thack;
1390102520Sdarrenr				ok = 1;
1391102520Sdarrenr			}
1392102520Sdarrenr		} else {
1393255332Scy			if (t->ftps_seq[0] + ackoff == thack) {
1394255332Scy				t->ftps_seq[0] = thack;
1395102520Sdarrenr				ok = 1;
1396255332Scy			} else if (t->ftps_seq[0] == thack + ackoff) {
1397255332Scy				t->ftps_seq[0] = thack + ackoff;
1398102520Sdarrenr				ok = 1;
1399255332Scy			} else if (t->ftps_seq[1] + ackoff == thack) {
1400255332Scy				t->ftps_seq[0] = thack;
1401102520Sdarrenr				ok = 1;
1402102520Sdarrenr			} else if (t->ftps_seq[1] == thack + ackoff) {
1403255332Scy				t->ftps_seq[0] = thack + ackoff;
1404102520Sdarrenr				ok = 1;
1405102520Sdarrenr			}
1406102520Sdarrenr		}
1407102520Sdarrenr	}
1408102520Sdarrenr
1409255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1410145522Sdarrenr		if (!ok)
1411145522Sdarrenr			printf("%s ok\n", "not");
1412145522Sdarrenr	}
1413102520Sdarrenr
1414102520Sdarrenr	if (!mlen) {
1415255332Scy		if (t->ftps_seq[0] + ackoff != thack &&
1416255332Scy		    t->ftps_seq[1] + ackoff != thack) {
1417255332Scy			DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack);
1418255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1419255332Scy				printf("%s:seq[0](%u) + (%d) != (%u)\n",
1420255332Scy				       "ipf_p_ftp_process", t->ftps_seq[0],
1421145522Sdarrenr				       ackoff, thack);
1422255332Scy				printf("%s:seq[0](%u) + (%d) != (%u)\n",
1423255332Scy				       "ipf_p_ftp_process", t->ftps_seq[1],
1424255332Scy				       ackoff, thack);
1425145522Sdarrenr			}
142695563Sdarrenr			return APR_ERR(1);
1427110916Sdarrenr		}
1428102520Sdarrenr
1429255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE) {
1430255332Scy			printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n",
1431145522Sdarrenr				f->ftps_seq[0], f->ftps_seq[1]);
1432145522Sdarrenr		}
1433145522Sdarrenr
1434102520Sdarrenr		if (tcp->th_flags & TH_FIN) {
1435110916Sdarrenr			if (thseq == f->ftps_seq[1]) {
1436110916Sdarrenr				f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1437110916Sdarrenr				f->ftps_seq[1] = thseq + 1 - seqoff;
1438110916Sdarrenr			} else {
1439255332Scy				DT2(thseq, ftpside_t *t, t, u_32_t, thseq);
1440255332Scy				if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1441255332Scy					printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1442255332Scy					       thseq, seqoff, f->ftps_seq[0]);
1443145522Sdarrenr				}
1444102520Sdarrenr				return APR_ERR(1);
1445102520Sdarrenr			}
144695563Sdarrenr		}
1447102520Sdarrenr		f->ftps_len = 0;
1448102520Sdarrenr		return 0;
144960857Sdarrenr	}
1450102520Sdarrenr
1451102520Sdarrenr	ok = 0;
1452110916Sdarrenr	if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1453102520Sdarrenr		ok = 1;
1454102520Sdarrenr	/*
1455102520Sdarrenr	 * Retransmitted data packet.
1456102520Sdarrenr	 */
1457110916Sdarrenr	} else if ((thseq + mlen == f->ftps_seq[0]) ||
1458110916Sdarrenr		   (thseq + mlen == f->ftps_seq[1])) {
1459102520Sdarrenr		ok = 1;
1460110916Sdarrenr	}
1461110916Sdarrenr
1462102520Sdarrenr	if (ok == 0) {
1463255332Scy		DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen);
1464102520Sdarrenr		inc = thseq - f->ftps_seq[0];
1465255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1466145522Sdarrenr			printf("inc %d sel %d rv %d\n", inc, sel, rv);
1467145522Sdarrenr			printf("th_seq %x ftps_seq %x/%x\n",
1468145522Sdarrenr			       thseq, f->ftps_seq[0], f->ftps_seq[1]);
1469145522Sdarrenr			printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1470145522Sdarrenr			       aps->aps_ackoff[sel]);
1471145522Sdarrenr			printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1472145522Sdarrenr			       aps->aps_seqoff[sel]);
1473145522Sdarrenr		}
1474102520Sdarrenr
1475102520Sdarrenr		return APR_ERR(1);
1476102520Sdarrenr	}
1477102520Sdarrenr
147895563Sdarrenr	inc = 0;
1479102520Sdarrenr	rptr = f->ftps_rptr;
1480102520Sdarrenr	wptr = f->ftps_wptr;
1481102520Sdarrenr	f->ftps_seq[0] = thseq;
1482102520Sdarrenr	f->ftps_seq[1] = f->ftps_seq[0] + mlen;
148372006Sdarrenr	f->ftps_len = mlen;
148460857Sdarrenr
148560857Sdarrenr	while (mlen > 0) {
1486145522Sdarrenr		len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1487255332Scy		if (len == 0)
1488255332Scy			break;
1489145522Sdarrenr		COPYDATA(m, off, len, wptr);
149060857Sdarrenr		mlen -= len;
149160857Sdarrenr		off += len;
149260857Sdarrenr		wptr += len;
1493145522Sdarrenr
1494255332Scywhilemore:
1495255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1496145522Sdarrenr			printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
1497255332Scy			       "ipf_p_ftp_process",
1498145522Sdarrenr			       len, mlen, off, (u_long)wptr, f->ftps_junk,
1499145522Sdarrenr			       len, len, rptr);
1500145522Sdarrenr
150160857Sdarrenr		f->ftps_wptr = wptr;
1502255332Scy		if (f->ftps_junk != FTPXY_JUNK_OK) {
1503145522Sdarrenr			i = f->ftps_junk;
1504255332Scy			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr,
1505110916Sdarrenr						      wptr - rptr);
1506255332Scy			DT2(junk_transit, int, i, int, f->ftps_junk);
150760857Sdarrenr
1508255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1509145522Sdarrenr				printf("%s:junk %d -> %d\n",
1510255332Scy				       "ipf_p_ftp_process", i, f->ftps_junk);
1511145522Sdarrenr
1512255332Scy			if (f->ftps_junk == FTPXY_JUNK_BAD) {
1513255332Scy				DT(buffer_full);
1514145522Sdarrenr				if (wptr - rptr == sizeof(f->ftps_buf)) {
1515255332Scy					if (softf->ipf_p_ftp_debug &
1516255332Scy					    DEBUG_PARSE_INFO)
1517145522Sdarrenr						printf("%s:full buffer\n",
1518255332Scy						       "ipf_p_ftp_process");
1519145522Sdarrenr					f->ftps_rptr = f->ftps_buf;
1520145522Sdarrenr					f->ftps_wptr = f->ftps_buf;
1521145522Sdarrenr					rptr = f->ftps_rptr;
1522145522Sdarrenr					wptr = f->ftps_wptr;
1523145522Sdarrenr					continue;
1524145522Sdarrenr				}
1525145522Sdarrenr			}
1526145522Sdarrenr		}
1527145522Sdarrenr
1528255332Scy		while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) {
1529145522Sdarrenr			len = wptr - rptr;
1530255332Scy			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv,
1531255332Scy						       rptr, len);
1532145522Sdarrenr
1533255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_PARSE) {
1534145522Sdarrenr				printf("%s=%d len %d rv %d ptr %lx/%lx ",
1535255332Scy				       "ipf_p_ftp_valid",
1536145522Sdarrenr				       f->ftps_junk, len, rv, (u_long)rptr,
1537145522Sdarrenr				       (u_long)wptr);
1538145522Sdarrenr				printf("buf [%*.*s]\n", len, len, rptr);
1539145522Sdarrenr			}
1540145522Sdarrenr
1541255332Scy			if (f->ftps_junk == FTPXY_JUNK_OK) {
1542255332Scy				f->ftps_cmds++;
154360857Sdarrenr				f->ftps_rptr = rptr;
154460857Sdarrenr				if (rv)
1545255332Scy					inc += ipf_p_ftp_server(softf, fin, ip,
1546255332Scy								nat, ftp, len);
154760857Sdarrenr				else
1548255332Scy					inc += ipf_p_ftp_client(softf, fin, ip,
1549255332Scy								nat, ftp, len);
155060857Sdarrenr				rptr = f->ftps_rptr;
155192685Sdarrenr				wptr = f->ftps_wptr;
155260857Sdarrenr			}
155360857Sdarrenr		}
155460857Sdarrenr
155592685Sdarrenr		/*
155692685Sdarrenr		 * Off to a bad start so lets just forget about using the
155792685Sdarrenr		 * ftp proxy for this connection.
155892685Sdarrenr		 */
1559255332Scy		if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) {
1560102520Sdarrenr			/* f->ftps_seq[1] += inc; */
1561145522Sdarrenr
1562255332Scy			DT(ftp_junk_cmd);
1563255332Scy			if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1564145522Sdarrenr				printf("%s:cmds == 0 junk == 1\n",
1565255332Scy				       "ipf_p_ftp_process");
156692685Sdarrenr			return APR_ERR(2);
156795563Sdarrenr		}
156892685Sdarrenr
1569255332Scy		retry = 0;
1570255332Scy		if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) {
1571145522Sdarrenr			for (s = rptr; s < wptr; s++) {
1572145522Sdarrenr				if ((*s == '\r') && (s + 1 < wptr) &&
1573145522Sdarrenr				    (*(s + 1) == '\n')) {
1574145522Sdarrenr					rptr = s + 2;
1575255332Scy					retry = 1;
1576255332Scy					if (f->ftps_junk != FTPXY_JUNK_CONT)
1577255332Scy						f->ftps_junk = FTPXY_JUNK_OK;
157867614Sdarrenr					break;
1579145522Sdarrenr				}
158060857Sdarrenr			}
158160857Sdarrenr		}
158260857Sdarrenr
158360857Sdarrenr		if (rptr == wptr) {
158460857Sdarrenr			rptr = wptr = f->ftps_buf;
158560857Sdarrenr		} else {
1586145522Sdarrenr			/*
1587145522Sdarrenr			 * Compact the buffer back to the start.  The junk
1588145522Sdarrenr			 * flag should already be set and because we're not
1589145522Sdarrenr			 * throwing away any data, it is preserved from its
1590145522Sdarrenr			 * current state.
1591145522Sdarrenr			 */
1592145522Sdarrenr			if (rptr > f->ftps_buf) {
1593255332Scy				bcopy(rptr, f->ftps_buf, wptr - rptr);
1594145522Sdarrenr				wptr -= rptr - f->ftps_buf;
1595145522Sdarrenr				rptr = f->ftps_buf;
159660857Sdarrenr			}
159760857Sdarrenr		}
1598145522Sdarrenr		f->ftps_rptr = rptr;
1599145522Sdarrenr		f->ftps_wptr = wptr;
1600255332Scy		if (retry)
1601255332Scy			goto whilemore;
160260857Sdarrenr	}
160360857Sdarrenr
1604102520Sdarrenr	/* f->ftps_seq[1] += inc; */
1605102520Sdarrenr	if (tcp->th_flags & TH_FIN)
1606102520Sdarrenr		f->ftps_seq[1]++;
1607255332Scy	if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) {
1608145522Sdarrenr		mlen = MSGDSIZE(m);
1609145522Sdarrenr		mlen -= off;
1610145522Sdarrenr		printf("ftps_seq[1] = %x inc %d len %d\n",
1611145522Sdarrenr		       f->ftps_seq[1], inc, mlen);
1612145522Sdarrenr	}
1613102520Sdarrenr
161460857Sdarrenr	f->ftps_rptr = rptr;
161560857Sdarrenr	f->ftps_wptr = wptr;
161664580Sdarrenr	return APR_INC(inc);
161760857Sdarrenr}
161860857Sdarrenr
161960857Sdarrenr
1620255332Scyint
1621255332Scyipf_p_ftp_out(arg, fin, aps, nat)
1622255332Scy	void *arg;
1623255332Scy	fr_info_t *fin;
1624255332Scy	ap_session_t *aps;
1625255332Scy	nat_t *nat;
162660857Sdarrenr{
1627255332Scy	ipf_ftp_softc_t *softf = arg;
162860857Sdarrenr	ftpinfo_t *ftp;
1629145522Sdarrenr	int rev;
163060857Sdarrenr
163160857Sdarrenr	ftp = aps->aps_data;
163260857Sdarrenr	if (ftp == NULL)
163360857Sdarrenr		return 0;
1634145522Sdarrenr
1635145522Sdarrenr	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1636145522Sdarrenr	if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1637145522Sdarrenr		ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1638145522Sdarrenr
1639255332Scy	return ipf_p_ftp_process(softf, fin, nat, ftp, rev);
164060857Sdarrenr}
164160857Sdarrenr
164260857Sdarrenr
1643255332Scyint
1644255332Scyipf_p_ftp_in(arg, fin, aps, nat)
1645255332Scy	void *arg;
1646255332Scy	fr_info_t *fin;
1647255332Scy	ap_session_t *aps;
1648255332Scy	nat_t *nat;
164953642Sguido{
1650255332Scy	ipf_ftp_softc_t *softf = arg;
165160857Sdarrenr	ftpinfo_t *ftp;
1652145522Sdarrenr	int rev;
165353642Sguido
165460857Sdarrenr	ftp = aps->aps_data;
165560857Sdarrenr	if (ftp == NULL)
165660857Sdarrenr		return 0;
1657145522Sdarrenr
1658145522Sdarrenr	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1659145522Sdarrenr	if (ftp->ftp_side[rev].ftps_ifp == NULL)
1660145522Sdarrenr		ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1661145522Sdarrenr
1662255332Scy	return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev);
166353642Sguido}
166460857Sdarrenr
166560857Sdarrenr
166660857Sdarrenr/*
1667255332Scy * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in
166860857Sdarrenr * pairs separated by commas (which are expected to be in the range 0 - 255),
166960857Sdarrenr * returning a 16 bit number combining either side of the , as the MSB and
167060857Sdarrenr * LSB.
167160857Sdarrenr */
1672255332Scyu_short
1673255332Scyipf_p_ftp_atoi(ptr)
1674255332Scy	char **ptr;
167560857Sdarrenr{
167660857Sdarrenr	register char *s = *ptr, c;
167760857Sdarrenr	register u_char i = 0, j = 0;
167860857Sdarrenr
1679145522Sdarrenr	while (((c = *s++) != '\0') && ISDIGIT(c)) {
168060857Sdarrenr		i *= 10;
168160857Sdarrenr		i += c - '0';
168260857Sdarrenr	}
168360857Sdarrenr	if (c != ',') {
168460857Sdarrenr		*ptr = NULL;
168560857Sdarrenr		return 0;
168660857Sdarrenr	}
1687145522Sdarrenr	while (((c = *s++) != '\0') && ISDIGIT(c)) {
168860857Sdarrenr		j *= 10;
168960857Sdarrenr		j += c - '0';
169060857Sdarrenr	}
169160857Sdarrenr	*ptr = s;
169267614Sdarrenr	i &= 0xff;
169367614Sdarrenr	j &= 0xff;
169460857Sdarrenr	return (i << 8) | j;
169560857Sdarrenr}
1696145522Sdarrenr
1697145522Sdarrenr
1698255332Scyint
1699255332Scyipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen)
1700255332Scy	ipf_ftp_softc_t *softf;
1701255332Scy	fr_info_t *fin;
1702255332Scy	ip_t *ip;
1703255332Scy	nat_t *nat;
1704255332Scy	ftpinfo_t *ftp;
1705255332Scy	int dlen;
1706145522Sdarrenr{
1707255332Scy	ftpside_t *f;
1708255332Scy
1709255332Scy	/*
1710255332Scy	 * Check for client sending out EPRT message.
1711255332Scy	 */
1712255332Scy	if (dlen < IPF_MINEPRTLEN) {
1713255332Scy		DT1(epert_dlen, int, dlen);
1714255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1715255332Scy			printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n",
1716255332Scy				dlen);
1717255332Scy		return 0;
1718255332Scy	}
1719255332Scy
1720255332Scy	/*
1721255332Scy	 * Parse the EPRT command.  Format is:
1722255332Scy	 * "EPRT |1|1.2.3.4|2000|" for IPv4 and
1723255332Scy	 * "EPRT |2|ef00::1:2|2000|" for IPv6
1724255332Scy	 */
1725255332Scy	f = &ftp->ftp_side[0];
1726255332Scy	if (f->ftps_rptr[5] != '|')
1727255332Scy		return 0;
1728255332Scy	if (f->ftps_rptr[5] == f->ftps_rptr[7]) {
1729255332Scy		if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4)
1730255332Scy			return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen);
1731255332Scy#ifdef USE_INET6
1732255332Scy		if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6)
1733255332Scy			return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen);
1734255332Scy#endif
1735255332Scy	}
1736255332Scy	return 0;
1737255332Scy}
1738255332Scy
1739255332Scy
1740255332Scyint
1741255332Scyipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen)
1742255332Scy	ipf_ftp_softc_t *softf;
1743255332Scy	fr_info_t *fin;
1744255332Scy	ip_t *ip;
1745255332Scy	nat_t *nat;
1746255332Scy	ftpinfo_t *ftp;
1747255332Scy	int dlen;
1748255332Scy{
1749255332Scy	int a1, a2, a3, a4, port, olen, nlen, inc, off;
1750145522Sdarrenr	char newbuf[IPF_FTPBUFSZ];
1751255332Scy	char *s, c, delim;
1752255332Scy	u_32_t addr, i;
1753255332Scy	tcphdr_t *tcp;
1754255332Scy	ftpside_t *f;
1755255332Scy	mb_t *m;
1756255332Scy
1757255332Scy	m = fin->fin_m;
1758255332Scy	tcp = (tcphdr_t *)fin->fin_dp;
1759255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1760255332Scy	f = &ftp->ftp_side[0];
1761255332Scy	delim = f->ftps_rptr[5];
1762255332Scy	s = f->ftps_rptr + 8;
1763255332Scy
1764255332Scy	/*
1765255332Scy	 * get the IP address.
1766255332Scy	 */
1767255332Scy	i = 0;
1768255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1769255332Scy		i *= 10;
1770255332Scy		i += c - '0';
1771255332Scy	}
1772255332Scy	if (i > 255)
1773255332Scy		return 0;
1774255332Scy	if (c != '.')
1775255332Scy		return 0;
1776255332Scy	addr = (i << 24);
1777255332Scy
1778255332Scy	i = 0;
1779255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1780255332Scy		i *= 10;
1781255332Scy		i += c - '0';
1782255332Scy	}
1783255332Scy	if (i > 255)
1784255332Scy		return 0;
1785255332Scy	if (c != '.')
1786255332Scy		return 0;
1787255332Scy	addr |= (addr << 16);
1788255332Scy
1789255332Scy	i = 0;
1790255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1791255332Scy		i *= 10;
1792255332Scy		i += c - '0';
1793255332Scy	}
1794255332Scy	if (i > 255)
1795255332Scy		return 0;
1796255332Scy	if (c != '.')
1797255332Scy		return 0;
1798255332Scy	addr |= (addr << 8);
1799255332Scy
1800255332Scy	i = 0;
1801255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1802255332Scy		i *= 10;
1803255332Scy		i += c - '0';
1804255332Scy	}
1805255332Scy	if (i > 255)
1806255332Scy		return 0;
1807255332Scy	if (c != delim)
1808255332Scy		return 0;
1809255332Scy	addr |= addr;
1810255332Scy
1811255332Scy	/*
1812255332Scy	 * Get the port number
1813255332Scy	 */
1814255332Scy	i = 0;
1815255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1816255332Scy		i *= 10;
1817255332Scy		i += c - '0';
1818255332Scy	}
1819255332Scy	if (i > 65535)
1820255332Scy		return 0;
1821255332Scy	if (c != delim)
1822255332Scy		return 0;
1823255332Scy	port = i;
1824255332Scy
1825255332Scy	/*
1826255332Scy	 * Check for CR-LF at the end of the command string.
1827255332Scy	 */
1828255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
1829255332Scy		DT(eprt4_no_crlf);
1830255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1831255332Scy			printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf");
1832255332Scy		return 0;
1833255332Scy	}
1834255332Scy	s += 2;
1835255332Scy
1836255332Scy	/*
1837255332Scy	 * Calculate new address parts for PORT command
1838255332Scy	 */
1839255332Scy	if (nat->nat_dir == NAT_INBOUND)
1840255332Scy		a1 = ntohl(nat->nat_odstaddr);
1841255332Scy	else
1842255332Scy		a1 = ntohl(ip->ip_src.s_addr);
1843255332Scy	a2 = (a1 >> 16) & 0xff;
1844255332Scy	a3 = (a1 >> 8) & 0xff;
1845255332Scy	a4 = a1 & 0xff;
1846255332Scy	a1 >>= 24;
1847255332Scy	olen = s - f->ftps_rptr;
1848255332Scy	/* DO NOT change this to snprintf! */
1849255332Scy	/*
1850255332Scy	 * While we could force the use of | as a delimiter here, it makes
1851255332Scy	 * sense to preserve whatever character is being used by the systems
1852255332Scy	 * involved in the communication.
1853255332Scy	 */
1854255332Scy#if defined(SNPRINTF) && defined(_KERNEL)
1855255332Scy	SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
1856255332Scy		 "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim);
1857255332Scy#else
1858255332Scy	(void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
1859255332Scy		       "EPRT", delim, delim, a1, a2, a3, a4, delim, port,
1860255332Scy			delim);
1861255332Scy#endif
1862255332Scy
1863255332Scy	nlen = strlen(newbuf);
1864255332Scy	inc = nlen - olen;
1865255332Scy	if ((inc + fin->fin_plen) > 65535) {
1866255332Scy		DT2(eprt4_len, int, inc, int, fin->fin_plen);
1867255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1868255332Scy			printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n",
1869255332Scy				inc);
1870255332Scy		return 0;
1871255332Scy	}
1872255332Scy
1873255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1874255332Scy#if !defined(_KERNEL)
1875255332Scy	M_ADJ(m, inc);
1876255332Scy#else
1877255332Scy	if (inc < 0)
1878255332Scy		M_ADJ(m, inc);
1879255332Scy#endif
1880255332Scy	/* the mbuf chain will be extended if necessary by m_copyback() */
1881255332Scy	COPYBACK(m, off, nlen, newbuf);
1882255332Scy	fin->fin_flx |= FI_DOCKSUM;
1883255332Scy
1884255332Scy	if (inc != 0) {
1885255332Scy		fin->fin_plen += inc;
1886255332Scy		ip->ip_len = htons(fin->fin_plen);
1887255332Scy		fin->fin_dlen += inc;
1888255332Scy	}
1889255332Scy
1890255332Scy	f->ftps_cmd = FTPXY_C_EPRT;
1891255332Scy	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
1892255332Scy}
1893255332Scy
1894255332Scy
1895255332Scyint
1896255332Scyipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen)
1897255332Scy	ipf_ftp_softc_t *softf;
1898255332Scy	fr_info_t *fin;
1899255332Scy	ip_t *ip;
1900255332Scy	nat_t *nat;
1901255332Scy	ftpinfo_t *ftp;
1902255332Scy	int dlen;
1903255332Scy{
1904255332Scy	char newbuf[IPF_FTPBUFSZ];
1905255332Scy	u_short ap = 0;
1906255332Scy	ftpside_t *f;
1907145522Sdarrenr	char *s;
1908145522Sdarrenr
1909255332Scy	if ((softf->ipf_p_ftp_forcepasv != 0) &&
1910255332Scy	    (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) {
1911255332Scy		DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd);
1912255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1913255332Scy			printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n",
1914255332Scy			       ftp->ftp_side[0].ftps_cmd);
1915255332Scy		return 0;
1916255332Scy	}
1917255332Scy	f = &ftp->ftp_side[1];
1918255332Scy
1919145522Sdarrenr#define EPSV_REPLEN	33
1920145522Sdarrenr	/*
1921145522Sdarrenr	 * Check for EPSV reply message.
1922145522Sdarrenr	 */
1923255332Scy	if (dlen < IPF_MIN229LEN) {
1924145522Sdarrenr		return (0);
1925255332Scy	} else if (strncmp(f->ftps_rptr,
1926255332Scy			 "229 Entering Extended Passive Mode", EPSV_REPLEN)) {
1927145522Sdarrenr		return (0);
1928255332Scy}
1929145522Sdarrenr
1930145522Sdarrenr	/*
1931145522Sdarrenr	 * Skip the EPSV command + space
1932145522Sdarrenr	 */
1933145522Sdarrenr	s = f->ftps_rptr + 33;
1934145522Sdarrenr	while (*s && !ISDIGIT(*s))
1935145522Sdarrenr		s++;
1936145522Sdarrenr
1937145522Sdarrenr	/*
1938145522Sdarrenr	 * As per RFC 2428, there are no addres components in the EPSV
1939145522Sdarrenr	 * response.  So we'll go straight to getting the port.
1940145522Sdarrenr	 */
1941145522Sdarrenr	while (*s && ISDIGIT(*s)) {
1942145522Sdarrenr		ap *= 10;
1943145522Sdarrenr		ap += *s++ - '0';
1944145522Sdarrenr	}
1945145522Sdarrenr
1946145522Sdarrenr	if (*s == '|')
1947145522Sdarrenr		s++;
1948145522Sdarrenr	if (*s == ')')
1949145522Sdarrenr		s++;
1950145522Sdarrenr	if (*s == '\n')
1951145522Sdarrenr		s--;
1952145522Sdarrenr	/*
1953145522Sdarrenr	 * check for CR-LF at the end.
1954145522Sdarrenr	 */
1955255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
1956145522Sdarrenr		return 0;
1957255332Scy	}
1958255332Scy	s += 2;
1959145522Sdarrenr
1960145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL)
1961145522Sdarrenr	SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1962145522Sdarrenr		 "229 Entering Extended Passive Mode", ap);
1963145522Sdarrenr#else
1964145522Sdarrenr	(void) sprintf(newbuf, "%s (|||%u|)\r\n",
1965145522Sdarrenr		       "229 Entering Extended Passive Mode", ap);
1966145522Sdarrenr#endif
1967145522Sdarrenr
1968255332Scy	return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap,
1969255332Scy				   newbuf, s);
1970145522Sdarrenr}
1971255332Scy
1972255332Scy#ifdef USE_INET6
1973255332Scyint
1974255332Scyipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen)
1975255332Scy	ipf_ftp_softc_t *softf;
1976255332Scy	fr_info_t *fin;
1977255332Scy	ip_t *ip;
1978255332Scy	nat_t *nat;
1979255332Scy	ftpinfo_t *ftp;
1980255332Scy	int dlen;
1981255332Scy{
1982255332Scy	int port, olen, nlen, inc, off, left, i;
1983255332Scy	char newbuf[IPF_FTPBUFSZ];
1984255332Scy	char *s, c;
1985255332Scy	i6addr_t addr, *a6;
1986255332Scy	tcphdr_t *tcp;
1987255332Scy	ip6_t *ip6;
1988255332Scy	char delim;
1989255332Scy	u_short whole;
1990255332Scy	u_short part;
1991255332Scy	ftpside_t *f;
1992255332Scy	u_short *t;
1993255332Scy	int fwd;
1994255332Scy	mb_t *m;
1995255332Scy	u_32_t a;
1996255332Scy
1997255332Scy	m = fin->fin_m;
1998255332Scy	ip6 = (ip6_t *)ip;
1999255332Scy	f = &ftp->ftp_side[0];
2000255332Scy	s = f->ftps_rptr + 8;
2001255332Scy	f = &ftp->ftp_side[0];
2002255332Scy	delim = f->ftps_rptr[5];
2003255332Scy	tcp = (tcphdr_t *)fin->fin_dp;
2004255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
2005255332Scy
2006255332Scy	addr.i6[0] = 0;
2007255332Scy	addr.i6[1] = 0;
2008255332Scy	addr.i6[2] = 0;
2009255332Scy	addr.i6[3] = 0;
2010255332Scy	/*
2011255332Scy	 * Parse an IPv6 address.
2012255332Scy	 * Go forward until either :: or | is found. If :: is found,
2013255332Scy	 * reverse direction. Direction change is performed to ease
2014255332Scy	 * parsing an unknown number of 0s in the middle.
2015255332Scy	 */
2016255332Scy	whole = 0;
2017255332Scy	t = (u_short *)&addr;
2018255332Scy	fwd = 1;
2019255332Scy	for (part = 0; (c = *s) != '\0'; ) {
2020255332Scy		if (c == delim) {
2021255332Scy			*t = htons((u_short)whole);
2022255332Scy			break;
2023255332Scy		}
2024255332Scy		if (c == ':') {
2025255332Scy			*t = part;
2026255332Scy			if (fwd) {
2027255332Scy				*t = htons((u_short)whole);
2028255332Scy				t++;
2029255332Scy			} else {
2030255332Scy				*t = htons((u_short)(whole >> 16));
2031255332Scy				t--;
2032255332Scy			}
2033255332Scy			whole = 0;
2034255332Scy			if (fwd == 1 && s[1] == ':') {
2035255332Scy				while (*s && *s != '|')
2036255332Scy					s++;
2037255332Scy				if ((c = *s) != delim)
2038255332Scy					break;
2039255332Scy				t = (u_short *)&addr.i6[3];
2040255332Scy				t++;
2041255332Scy				fwd = 0;
2042255332Scy			} else if (fwd == 0 && s[-1] == ':') {
2043255332Scy				break;
2044255332Scy			}
2045255332Scy		} else {
2046255332Scy			if (c >= '0' && c <= '9') {
2047255332Scy				c -= '0';
2048255332Scy			} else if (c >= 'a' && c <= 'f') {
2049255332Scy				c -= 'a' + 10;
2050255332Scy			} else if (c >= 'A' && c <= 'F') {
2051255332Scy				c -= 'A' + 10;
2052255332Scy			}
2053255332Scy			if (fwd) {
2054255332Scy				whole <<= 8;
2055255332Scy				whole |= c;
2056255332Scy			} else {
2057255332Scy				whole >>= 8;
2058255332Scy				whole |= ((u_32_t)c) << 24;
2059255332Scy			}
2060255332Scy		}
2061255332Scy		if (fwd)
2062255332Scy			s++;
2063255332Scy		else
2064255332Scy			s--;
2065255332Scy	}
2066255332Scy	if (c != ':' && c != delim)
2067255332Scy		return 0;
2068255332Scy
2069255332Scy	while (*s != '|')
2070255332Scy		s++;
2071255332Scy	s++;
2072255332Scy
2073255332Scy	/*
2074255332Scy	 * Get the port number
2075255332Scy	 */
2076255332Scy	i = 0;
2077255332Scy	while (((c = *s++) != '\0') && ISDIGIT(c)) {
2078255332Scy		i *= 10;
2079255332Scy		i += c - '0';
2080255332Scy	}
2081255332Scy	if (i > 65535)
2082255332Scy		return 0;
2083255332Scy	if (c != delim)
2084255332Scy		return 0;
2085255332Scy	port = (u_short)(i & 0xffff);
2086255332Scy
2087255332Scy	/*
2088255332Scy	 * Check for CR-LF at the end of the command string.
2089255332Scy	 */
2090255332Scy	if ((*s != '\r') || (*(s + 1) != '\n')) {
2091255332Scy		DT(eprt6_no_crlf);
2092255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
2093255332Scy			printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf");
2094255332Scy		return 0;
2095255332Scy	}
2096255332Scy	s += 2;
2097255332Scy
2098255332Scy	/*
2099255332Scy	 * Calculate new address parts for PORT command
2100255332Scy	 */
2101255332Scy	a6 = (i6addr_t *)&ip6->ip6_src;
2102255332Scy	olen = s - f->ftps_rptr;
2103255332Scy	/* DO NOT change this to snprintf! */
2104255332Scy	/*
2105255332Scy	 * While we could force the use of | as a delimiter here, it makes
2106255332Scy	 * sense to preserve whatever character is being used by the systems
2107255332Scy	 * involved in the communication.
2108255332Scy	 */
2109255332Scy	s = newbuf;
2110255332Scy	left = sizeof(newbuf);
2111255332Scy#if defined(SNPRINTF) && defined(_KERNEL)
2112255332Scy	SNPRINTF(newbuf, left, "EPRT %c2%c", delim, delim);
2113255332Scy	left -= strlen(s) + 1;
2114255332Scy	s += strlen(s);
2115255332Scy	a = ntohl(a6->i6[0]);
2116255332Scy	SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff);
2117255332Scy	left -= strlen(s);
2118255332Scy	s += strlen(s);
2119255332Scy	a = ntohl(a6->i6[1]);
2120255332Scy	SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff);
2121255332Scy	left -= strlen(s);
2122255332Scy	s += strlen(s);
2123255332Scy	a = ntohl(a6->i6[2]);
2124255332Scy	SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff);
2125255332Scy	left -= strlen(s);
2126255332Scy	s += strlen(s);
2127255332Scy	a = ntohl(a6->i6[3]);
2128255332Scy	SNPRINTF(s, left, "%x:%x", a >> 16, a & 0xffff);
2129255332Scy	left -= strlen(s);
2130255332Scy	s += strlen(s);
2131328203Scy	SNPRINTF(s, left, "|%d|\r\n", port);
2132255332Scy#else
2133255332Scy	(void) sprintf(s, "EPRT %c2%c", delim, delim);
2134255332Scy	s += strlen(s);
2135255332Scy	a = ntohl(a6->i6[0]);
2136255332Scy	sprintf(s, "%x:%x:", a >> 16, a & 0xffff);
2137255332Scy	s += strlen(s);
2138255332Scy	a = ntohl(a6->i6[1]);
2139255332Scy	sprintf(s, "%x:%x:", a >> 16, a & 0xffff);
2140255332Scy	left -= strlen(s);
2141255332Scy	s += strlen(s);
2142255332Scy	a = ntohl(a6->i6[2]);
2143255332Scy	sprintf(s, "%x:%x:", a >> 16, a & 0xffff);
2144255332Scy	left -= strlen(s);
2145255332Scy	s += strlen(s);
2146255332Scy	a = ntohl(a6->i6[3]);
2147255332Scy	sprintf(s, "%x:%x", a >> 16, a & 0xffff);
2148255332Scy	left -= strlen(s);
2149255332Scy	s += strlen(s);
2150255332Scy	sprintf(s, "|%d|\r\n", port);
2151255332Scy#endif
2152255332Scy	nlen = strlen(newbuf);
2153255332Scy	inc = nlen - olen;
2154255332Scy	if ((inc + fin->fin_plen) > 65535) {
2155255332Scy		DT2(eprt6_len, int, inc, int, fin->fin_plen);
2156255332Scy		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
2157255332Scy			printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n",
2158255332Scy				inc);
2159255332Scy		return 0;
2160255332Scy	}
2161255332Scy
2162255332Scy	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
2163255332Scy#if !defined(_KERNEL)
2164255332Scy	M_ADJ(m, inc);
2165255332Scy#else
2166255332Scy	if (inc < 0)
2167255332Scy		M_ADJ(m, inc);
2168255332Scy#endif
2169255332Scy	/* the mbuf chain will be extended if necessary by m_copyback() */
2170255332Scy	COPYBACK(m, off, nlen, newbuf);
2171255332Scy	fin->fin_flx |= FI_DOCKSUM;
2172255332Scy
2173255332Scy	if (inc != 0) {
2174255332Scy		fin->fin_plen += inc;
2175255332Scy		ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen);
2176255332Scy		fin->fin_dlen += inc;
2177255332Scy	}
2178255332Scy
2179255332Scy	f->ftps_cmd = FTPXY_C_EPRT;
2180255332Scy	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
2181255332Scy}
2182255332Scy#endif
2183