xref: /netbsd-src/sys/external/bsd/ipf/netinet/ip_ftp_pxy.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
1 /*	$NetBSD: ip_ftp_pxy.c,v 1.5 2014/03/20 20:43:12 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2012 by Darren Reed.
5  *
6  * See the IPFILTER.LICENCE file for details on licencing.
7  *
8  * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
9  * code.
10  *
11  * Id: ip_ftp_pxy.c,v 1.1.1.2 2012/07/22 13:45:18 darrenr Exp
12  */
13 
14 #include <sys/cdefs.h>
15 __KERNEL_RCSID(1, "$NetBSD: ip_ftp_pxy.c,v 1.5 2014/03/20 20:43:12 christos Exp $");
16 
17 #define	IPF_FTP_PROXY
18 
19 #define	IPF_MINPORTLEN	18
20 #define	IPF_MINEPRTLEN	20
21 #define	IPF_MAXPORTLEN	30
22 #define	IPF_MIN227LEN	39
23 #define	IPF_MAX227LEN	51
24 #define	IPF_MIN229LEN	47
25 #define	IPF_MAX229LEN	51
26 
27 #define	FTPXY_GO	0
28 #define	FTPXY_INIT	1
29 #define	FTPXY_USER_1	2
30 #define	FTPXY_USOK_1	3
31 #define	FTPXY_PASS_1	4
32 #define	FTPXY_PAOK_1	5
33 #define	FTPXY_AUTH_1	6
34 #define	FTPXY_AUOK_1	7
35 #define	FTPXY_ADAT_1	8
36 #define	FTPXY_ADOK_1	9
37 #define	FTPXY_ACCT_1	10
38 #define	FTPXY_ACOK_1	11
39 #define	FTPXY_USER_2	12
40 #define	FTPXY_USOK_2	13
41 #define	FTPXY_PASS_2	14
42 #define	FTPXY_PAOK_2	15
43 
44 #define	FTPXY_JUNK_OK	0
45 #define	FTPXY_JUNK_BAD	1	/* Ignore all commands for this connection */
46 #define	FTPXY_JUNK_EOL	2	/* consume the rest of this line only */
47 #define	FTPXY_JUNK_CONT	3	/* Saerching for next numeric */
48 
49 /*
50  * Values for FTP commands.  Numerics cover 0-999
51  */
52 #define	FTPXY_C_PASV	1000
53 #define	FTPXY_C_PORT	1001
54 #define	FTPXY_C_EPSV	1002
55 #define	FTPXY_C_EPRT	1003
56 
57 
58 typedef struct ipf_ftp_softc_s {
59 	int	ipf_p_ftp_pasvonly;
60 	/* Do not require logins before transfers */
61 	int	ipf_p_ftp_insecure;
62 	int	ipf_p_ftp_pasvrdr;
63 	/* PASV must be last command prior to 227 */
64 	int	ipf_p_ftp_forcepasv;
65 	int	ipf_p_ftp_debug;
66 	int	ipf_p_ftp_single_xfer;
67 	void	*ipf_p_ftp_tune;
68 } ipf_ftp_softc_t;
69 
70 
71 void ipf_p_ftp_main_load(void);
72 void ipf_p_ftp_main_unload(void);
73 void *ipf_p_ftp_soft_create(ipf_main_softc_t *);
74 void ipf_p_ftp_soft_destroy(ipf_main_softc_t *, void *);
75 
76 int ipf_p_ftp_client(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
77 			  ftpinfo_t *, int);
78 int ipf_p_ftp_complete(char *, size_t);
79 int ipf_p_ftp_in(void *, fr_info_t *, ap_session_t *, nat_t *);
80 int ipf_p_ftp_new(void *, fr_info_t *, ap_session_t *, nat_t *);
81 void ipf_p_ftp_del(ipf_main_softc_t *, ap_session_t *);
82 int ipf_p_ftp_out(void *, fr_info_t *, ap_session_t *, nat_t *);
83 int ipf_p_ftp_pasv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
84 			ftpinfo_t *, int);
85 int ipf_p_ftp_epsv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
86 			ftpinfo_t *, int);
87 int ipf_p_ftp_port(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
88 			ftpinfo_t *, int);
89 int ipf_p_ftp_process(ipf_ftp_softc_t *, fr_info_t *, nat_t *,
90 			   ftpinfo_t *, int);
91 int ipf_p_ftp_server(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
92 			  ftpinfo_t *, int);
93 int ipf_p_ftp_valid(ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t);
94 int ipf_p_ftp_server_valid(ipf_ftp_softc_t *, ftpside_t *, char *,
95 				size_t);
96 int ipf_p_ftp_client_valid(ipf_ftp_softc_t *, ftpside_t *, char *,
97 				size_t);
98 u_short ipf_p_ftp_atoi(char **);
99 int ipf_p_ftp_pasvreply(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
100 			     ftpinfo_t *, u_int, char *, char *);
101 int ipf_p_ftp_eprt(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
102 			ftpinfo_t *, int);
103 int ipf_p_ftp_eprt4(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
104 			 ftpinfo_t *, int);
105 int ipf_p_ftp_eprt6(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
106 			 ftpinfo_t *, int);
107 int ipf_p_ftp_addport(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
108 			   ftpinfo_t *, int, int, int);
109 void ipf_p_ftp_setpending(ipf_main_softc_t *, ftpinfo_t *);
110 
111 /*
112  * Debug levels
113  */
114 #define	DEBUG_SECURITY		0x01
115 #define	DEBUG_ERROR		0x02
116 #define	DEBUG_INFO		0x04
117 #define	DEBUG_PARSE_ERR		0x08
118 #define	DEBUG_PARSE_INFO	0x10
119 #define	DEBUG_PARSE		0x20
120 
121 static	int	ipf_p_ftp_proxy_init = 0;
122 static	frentry_t	ftppxyfr;
123 static	ipftuneable_t	ipf_ftp_tuneables[] = {
124 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) },
125 		"ftp_debug",	0,	0x7f,
126 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug),
127 		0, NULL, NULL },
128 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) },
129 		"ftp_pasvonly",	0,	1,
130 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly),
131 		0, NULL, NULL },
132 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) },
133 		"ftp_insecure",	0,	1,
134 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure),
135 		0, NULL, NULL },
136 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) },
137 		"ftp_pasvrdr",	0,	1,
138 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr),
139 		0, NULL, NULL },
140 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) },
141 		"ftp_forcepasv", 0,	1,
142 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv),
143 		0, NULL, NULL },
144 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) },
145 		"ftp_single_xfer", 0,	1,
146 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer),
147 		0, NULL, NULL },
148 	{ { NULL }, NULL, 0, 0, 0, 0, NULL, NULL }
149 };
150 
151 
152 void
153 ipf_p_ftp_main_load(void)
154 {
155 	bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
156 	ftppxyfr.fr_ref = 1;
157 	ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
158 
159 	MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex");
160 	ipf_p_ftp_proxy_init = 1;
161 }
162 
163 
164 void
165 ipf_p_ftp_main_unload(void)
166 {
167 
168 	if (ipf_p_ftp_proxy_init == 1) {
169 		MUTEX_DESTROY(&ftppxyfr.fr_lock);
170 		ipf_p_ftp_proxy_init = 0;
171 	}
172 }
173 
174 
175 /*
176  * Initialize local structures.
177  */
178 void *
179 ipf_p_ftp_soft_create(ipf_main_softc_t *softc)
180 {
181 	ipf_ftp_softc_t *softf;
182 
183 	KMALLOC(softf, ipf_ftp_softc_t *);
184 	if (softf == NULL)
185 		return NULL;
186 
187 	bzero((char *)softf, sizeof(*softf));
188 #if defined(_KERNEL)
189 	softf->ipf_p_ftp_debug = 0;
190 #else
191 	softf->ipf_p_ftp_debug = DEBUG_PARSE_ERR;
192 #endif
193 	softf->ipf_p_ftp_forcepasv = 1;
194 
195 	softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf,
196 						    sizeof(ipf_ftp_tuneables),
197 						    ipf_ftp_tuneables);
198 	if (softf->ipf_p_ftp_tune == NULL) {
199 		ipf_p_ftp_soft_destroy(softc, softf);
200 		return NULL;
201 	}
202 	if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) {
203 		ipf_p_ftp_soft_destroy(softc, softf);
204 		return NULL;
205 	}
206 
207 	return softf;
208 }
209 
210 
211 void
212 ipf_p_ftp_soft_destroy(ipf_main_softc_t *softc, void *arg)
213 {
214 	ipf_ftp_softc_t *softf = arg;
215 
216 	if (softf->ipf_p_ftp_tune != NULL) {
217 		ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune);
218 		KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables));
219 		softf->ipf_p_ftp_tune = NULL;
220 	}
221 
222 	KFREE(softf);
223 }
224 
225 
226 int
227 ipf_p_ftp_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
228 {
229 	ftpinfo_t *ftp;
230 	ftpside_t *f;
231 
232 	KMALLOC(ftp, ftpinfo_t *);
233 	if (ftp == NULL)
234 		return -1;
235 
236 	nat = nat;	/* LINT */
237 
238 	aps->aps_data = ftp;
239 	aps->aps_psiz = sizeof(ftpinfo_t);
240 	aps->aps_sport = htons(fin->fin_sport);
241 	aps->aps_dport = htons(fin->fin_dport);
242 
243 	bzero((char *)ftp, sizeof(*ftp));
244 	f = &ftp->ftp_side[0];
245 	f->ftps_rptr = f->ftps_buf;
246 	f->ftps_wptr = f->ftps_buf;
247 	f = &ftp->ftp_side[1];
248 	f->ftps_rptr = f->ftps_buf;
249 	f->ftps_wptr = f->ftps_buf;
250 	ftp->ftp_passok = FTPXY_INIT;
251 	ftp->ftp_incok = 0;
252 	return 0;
253 }
254 
255 
256 void
257 ipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp)
258 {
259 	if (ftp->ftp_pendnat != NULL)
260 		ipf_nat_setpending(softc, ftp->ftp_pendnat);
261 
262 	if (ftp->ftp_pendstate != NULL) {
263 		READ_ENTER(&softc->ipf_state);
264 		ipf_state_setpending(softc, ftp->ftp_pendstate);
265 		RWLOCK_EXIT(&softc->ipf_state);
266 	}
267 }
268 
269 
270 void
271 ipf_p_ftp_del(ipf_main_softc_t *softc, ap_session_t *aps)
272 {
273 	ftpinfo_t *ftp;
274 
275 	ftp = aps->aps_data;
276 	if (ftp != NULL)
277 		ipf_p_ftp_setpending(softc, ftp);
278 }
279 
280 
281 int
282 ipf_p_ftp_port(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
283     ftpinfo_t *ftp, int dlen)
284 {
285 	char newbuf[IPF_FTPBUFSZ], *s;
286 	u_int a1, a2, a3, a4;
287 	u_short a5, a6, sp;
288 	size_t nlen, olen;
289 	tcphdr_t *tcp;
290 	int inc, off;
291 	ftpside_t *f;
292 	mb_t *m;
293 
294 	m = fin->fin_m;
295 	f = &ftp->ftp_side[0];
296 	tcp = (tcphdr_t *)fin->fin_dp;
297 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
298 
299 	/*
300 	 * Check for client sending out PORT message.
301 	 */
302 	if (dlen < IPF_MINPORTLEN) {
303 		DT3(ftp_PORT_error_dlen, nat_t *, nat, ftpside_t *, f,
304 		    u_int, dlen);
305 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
306 			printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
307 			       dlen);
308 		return 0;
309 	}
310 	/*
311 	 * Skip the PORT command + space
312 	 */
313 	s = f->ftps_rptr + 5;
314 	/*
315 	 * Pick out the address components, two at a time.
316 	 */
317 	a1 = ipf_p_ftp_atoi(&s);
318 	if (s == NULL) {
319 		DT2(ftp_PORT_error_atoi_1, nat_t *, nat, ftpside_t *, f);
320 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
321 			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1);
322 		return 0;
323 	}
324 	a2 = ipf_p_ftp_atoi(&s);
325 	if (s == NULL) {
326 		DT2(ftp_PORT_error_atoi_2, nat_t *, nat, ftpside_t *, f);
327 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
328 			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2);
329 		return 0;
330 	}
331 
332 	/*
333 	 * Check that IP address in the PORT/PASV reply is the same as the
334 	 * sender of the command - prevents using PORT for port scanning.
335 	 */
336 	a1 <<= 16;
337 	a1 |= a2;
338 	if (((nat->nat_dir == NAT_OUTBOUND) &&
339 	     (a1 != ntohl(nat->nat_osrcaddr))) ||
340 	    ((nat->nat_dir == NAT_INBOUND) &&
341 	     (a1 != ntohl(nat->nat_nsrcaddr)))) {
342 		DT3(ftp_PORT_error_address, nat_t *, nat, ftpside_t *, f,
343 		    u_int, a1);
344 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
345 			printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1");
346 		return APR_ERR(1);
347 	}
348 
349 	a5 = ipf_p_ftp_atoi(&s);
350 	if (s == NULL) {
351 		DT2(ftp_PORT_error_atoi_3, nat_t *, nat, ftpside_t *, f);
352 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
353 			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3);
354 		return 0;
355 	}
356 	if (*s == ')')
357 		s++;
358 
359 	/*
360 	 * check for CR-LF at the end.
361 	 */
362 	if (*s == '\n')
363 		s--;
364 	if ((*s != '\r') || (*(s + 1) != '\n')) {
365 		DT2(ftp_PORT_error_no_crlf, nat_t *, nat, ftpside_t *, f);
366 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
367 			printf("ipf_p_ftp_port:missing %s\n", "cr-lf");
368 		return 0;
369 	}
370 	s += 2;
371 	a6 = a5 & 0xff;
372 
373 	/*
374 	 * Calculate the source port. Verification of > 1024 is in
375 	 * ipf_p_ftp_addport.
376 	 */
377 	a5 >>= 8;
378 	a5 &= 0xff;
379 	sp = a5 << 8 | a6;
380 
381 	/*
382 	 * Calculate new address parts for PORT command
383 	 */
384 	if (nat->nat_dir == NAT_INBOUND)
385 		a1 = ntohl(nat->nat_ndstaddr);
386 	else
387 		a1 = ntohl(ip->ip_src.s_addr);
388 	a1 = ntohl(ip->ip_src.s_addr);
389 	a2 = (a1 >> 16) & 0xff;
390 	a3 = (a1 >> 8) & 0xff;
391 	a4 = a1 & 0xff;
392 	a1 >>= 24;
393 	olen = s - f->ftps_rptr;
394 	snprintf(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
395 		 "PORT", a1, a2, a3, a4, a5, a6);
396 
397 	nlen = strlen(newbuf);
398 	inc = nlen - olen;
399 	if ((inc + fin->fin_plen) > 65535) {
400 		DT3(ftp_PORT_error_inc, nat_t *, nat, ftpside_t *, f,
401 		    int, inc);
402 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
403 			printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n",
404 			       inc);
405 		return 0;
406 	}
407 
408 #if !defined(_KERNEL)
409 	M_ADJ(m, inc);
410 #else
411 	/*
412 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
413 	 * mean remove -len bytes from the end of the packet.
414 	 * The mbuf chain will be extended if necessary by m_copyback().
415 	 */
416 	if (inc < 0)
417 		M_ADJ(m, inc);
418 #endif /* !defined(_KERNEL) */
419 	COPYBACK(m, off, nlen, newbuf);
420 	fin->fin_flx |= FI_DOCKSUM;
421 
422 	if (inc != 0) {
423 		fin->fin_plen += inc;
424 		ip->ip_len = htons(fin->fin_plen);
425 		fin->fin_dlen += inc;
426 	}
427 
428 	f->ftps_cmd = FTPXY_C_PORT;
429 	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, sp, inc);
430 }
431 
432 
433 int
434 ipf_p_ftp_addport(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
435     ftpinfo_t *ftp, int dlen, int nport, int inc)
436 {
437 	tcphdr_t tcph, *tcp2 = &tcph;
438 	ipf_main_softc_t *softc;
439 	ipf_nat_softc_t *softn;
440 	int direction;
441 	fr_info_t fi;
442 	ipnat_t *ipn;
443 	nat_t *nat2;
444 	u_short sp;
445 	int flags;
446 
447 	softc = fin->fin_main_soft;
448 	softn = softc->ipf_nat_soft;
449 
450 	if ((ftp->ftp_pendnat != NULL)  || (ftp->ftp_pendstate != NULL)) {
451 		if (softf->ipf_p_ftp_single_xfer != 0) {
452 			DT2(ftp_PORT_error_add_active, nat_t *, nat,
453 			    ftpinfo_t *, ftp);
454 			if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
455 				printf("ipf_p_ftp_addport:xfer active %p/%p\n",
456 				       ftp->ftp_pendnat, ftp->ftp_pendstate);
457 			return 0;
458 		}
459 		ipf_p_ftp_setpending(softc, ftp);
460 	}
461 
462 	/*
463 	 * Add skeleton NAT entry for connection which will come back the
464 	 * other way.
465 	 */
466 	sp = nport;
467 	/*
468 	 * Don't allow the PORT command to specify a port < 1024 due to
469 	 * security risks.
470 	 */
471 	if (sp < 1024) {
472 		DT3(ftp_PORT_error_port, nat_t *, nat, ftpinfo_t *, ftp,
473 		    u_int, sp);
474 		if (softf->ipf_p_ftp_debug & DEBUG_SECURITY)
475 			printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp);
476 		return 0;
477 	}
478 	/*
479 	 * The server may not make the connection back from port 20, but
480 	 * it is the most likely so use it here to check for a conflicting
481 	 * mapping.
482 	 */
483 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
484 	fi.fin_flx |= FI_IGNORE;
485 	fi.fin_data[0] = sp;
486 	fi.fin_data[1] = fin->fin_data[1] - 1;
487 	fi.fin_src6 = nat->nat_ndst6;
488 	fi.fin_dst6 = nat->nat_nsrc6;
489 
490 	if (nat->nat_v[0] == 6) {
491 #ifndef USE_INET6
492 		return APR_INC(inc);
493 #endif
494 	}
495 
496 	/*
497 	 * Add skeleton NAT entry for connection which will come back the
498 	 * other way.
499 	 */
500 	if (nat->nat_v[0] == 6) {
501 #ifdef USE_INET6
502 		if (nat->nat_dir == NAT_OUTBOUND) {
503 			nat2 = ipf_nat6_outlookup(&fi, IPN_TCP|NAT_SEARCH,
504 						  nat->nat_pr[1],
505 						  &nat->nat_osrc6.in6,
506 						  &nat->nat_odst6.in6);
507 		} else {
508 			nat2 = ipf_nat6_inlookup(&fi, IPN_TCP|NAT_SEARCH,
509 						 nat->nat_pr[0],
510 						 &nat->nat_odst6.in6,
511 						 &nat->nat_osrc6.in6);
512 		}
513 #endif
514 	} else {
515 		if (nat->nat_dir == NAT_OUTBOUND) {
516 			nat2 = ipf_nat_outlookup(&fi, IPN_TCP|NAT_SEARCH,
517 						 nat->nat_pr[1],
518 						 nat->nat_osrcip,
519 						 nat->nat_odstip);
520 		} else {
521 			nat2 = ipf_nat_inlookup(&fi, IPN_TCP|NAT_SEARCH,
522 						nat->nat_pr[0],
523 						nat->nat_odstip,
524 						nat->nat_osrcip);
525 		}
526 	}
527 	if (nat2 != NULL)
528 		return APR_INC(inc);
529 
530 	ipn = ipf_proxy_rule_rev(nat);
531 	if (ipn == NULL)
532 		return APR_ERR(1);
533 	ipn->in_use = 0;
534 
535 	fi.fin_fr = &ftppxyfr;
536 	fi.fin_dp = (char *)tcp2;
537 	fi.fin_dlen = sizeof(*tcp2);
538 	fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
539 	fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
540 	fi.fin_data[1] = sp;
541 	fi.fin_data[0] = 0;
542 
543 	bzero((char *)tcp2, sizeof(*tcp2));
544 	tcp2->th_sport = 0;
545 	tcp2->th_dport = htons(sp);
546 
547 	tcp2->th_win = htons(8192);
548 	TCP_OFF_A(tcp2, 5);
549 	tcp2->th_flags = TH_SYN;
550 
551 	if (nat->nat_dir == NAT_INBOUND) {
552 		fi.fin_out = 1;
553 		direction = NAT_OUTBOUND;
554 	} else {
555 		fi.fin_out = 0;
556 		direction = NAT_INBOUND;
557 	}
558 	flags = SI_W_SPORT|NAT_SLAVE|IPN_TCP;
559 
560 	MUTEX_ENTER(&softn->ipf_nat_new);
561 	if (nat->nat_v[0] == 6) {
562 #ifdef USE_INET6
563 		nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, flags,
564 				    direction);
565 #endif
566 	} else {
567 		nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, flags,
568 				   direction);
569 	}
570 	MUTEX_EXIT(&softn->ipf_nat_new);
571 
572 	if (nat2 == NULL) {
573 		KFREES(ipn, ipn->in_size);
574 		return APR_ERR(1);
575 	}
576 
577 	(void) ipf_nat_proto(&fi, nat2, IPN_TCP);
578 	MUTEX_ENTER(&nat2->nat_lock);
579 	ipf_nat_update(&fi, nat2);
580 	MUTEX_EXIT(&nat2->nat_lock);
581 	fi.fin_ifp = NULL;
582 	if (nat2->nat_dir == NAT_INBOUND)
583 		fi.fin_dst6 = nat->nat_osrc6;
584 	if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate,
585 			  SI_W_SPORT) != 0)
586 		ipf_nat_setpending(softc, nat2);
587 
588 	return APR_INC(inc);
589 }
590 
591 
592 int
593 ipf_p_ftp_client(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
594     ftpinfo_t *ftp, int dlen)
595 {
596 	char *rptr, *wptr, cmd[6], c;
597 	ftpside_t *f;
598 	int inc, i;
599 
600 	inc = 0;
601 	f = &ftp->ftp_side[0];
602 	rptr = f->ftps_rptr;
603 	wptr = f->ftps_wptr;
604 
605 	for (i = 0; (i < 5) && (i < dlen); i++) {
606 		c = rptr[i];
607 		if (ISALPHA(c)) {
608 			cmd[i] = TOUPPER(c);
609 		} else {
610 			cmd[i] = c;
611 		}
612 	}
613 	cmd[i] = '\0';
614 
615 	ftp->ftp_incok = 0;
616 	DT2(ftp_client_command, char [], cmd, int, ftp->ftp_passok);
617 	if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
618 		if (ftp->ftp_passok == FTPXY_ADOK_1 ||
619 		    ftp->ftp_passok == FTPXY_AUOK_1) {
620 			ftp->ftp_passok = FTPXY_USER_2;
621 			ftp->ftp_incok = 1;
622 		} else {
623 			ftp->ftp_passok = FTPXY_USER_1;
624 			ftp->ftp_incok = 1;
625 		}
626 	} else if (!strncmp(cmd, "AUTH ", 5)) {
627 		ftp->ftp_passok = FTPXY_AUTH_1;
628 		ftp->ftp_incok = 1;
629 	} else if (!strncmp(cmd, "PASS ", 5)) {
630 		if (ftp->ftp_passok == FTPXY_USOK_1) {
631 			ftp->ftp_passok = FTPXY_PASS_1;
632 			ftp->ftp_incok = 1;
633 		} else if (ftp->ftp_passok == FTPXY_USOK_2) {
634 			ftp->ftp_passok = FTPXY_PASS_2;
635 			ftp->ftp_incok = 1;
636 		}
637 	} else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
638 		   !strncmp(cmd, "ADAT ", 5)) {
639 		ftp->ftp_passok = FTPXY_ADAT_1;
640 		ftp->ftp_incok = 1;
641 	} else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
642 		    ftp->ftp_passok == FTPXY_PAOK_2) &&
643 		 !strncmp(cmd, "ACCT ", 5)) {
644 		ftp->ftp_passok = FTPXY_ACCT_1;
645 		ftp->ftp_incok = 1;
646 	} else if ((ftp->ftp_passok == FTPXY_GO) &&
647 		   !softf->ipf_p_ftp_pasvonly &&
648 		 !strncmp(cmd, "PORT ", 5)) {
649 		inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
650 	} else if ((ftp->ftp_passok == FTPXY_GO) &&
651 		   !softf->ipf_p_ftp_pasvonly &&
652 		 !strncmp(cmd, "EPRT ", 5)) {
653 		inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen);
654 	} else if (softf->ipf_p_ftp_insecure &&
655 		   !softf->ipf_p_ftp_pasvonly &&
656 		   !strncmp(cmd, "PORT ", 5)) {
657 		inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
658 	}
659 	if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
660 		printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n",
661 		       cmd, ftp->ftp_passok, ftp->ftp_incok, inc);
662 
663 	DT2(ftp_client_passok, char *, cmd, int, ftp->ftp_passok);
664 	while ((*rptr++ != '\n') && (rptr < wptr))
665 		;
666 	f->ftps_rptr = rptr;
667 	return inc;
668 }
669 
670 
671 int
672 ipf_p_ftp_pasv(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
673     ftpinfo_t *ftp, int dlen)
674 {
675 	u_int a1, a2, a3, a4, data_ip;
676 	char newbuf[IPF_FTPBUFSZ];
677 	const char *brackets[2];
678 	u_short a5, a6;
679 	ftpside_t *f;
680 	char *s;
681 
682 	if ((softf->ipf_p_ftp_forcepasv != 0) &&
683 	    (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) {
684 		DT2(ftp_PASV_error_state, nat_t *, nat, ftpinfo_t *, ftp);
685 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
686 			printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n",
687 			       ftp->ftp_side[0].ftps_cmd);
688 		return 0;
689 	}
690 
691 	f = &ftp->ftp_side[1];
692 
693 #define	PASV_REPLEN	24
694 	/*
695 	 * Check for PASV reply message.
696 	 */
697 	if (dlen < IPF_MIN227LEN) {
698 		DT3(ftp_PASV_error_short, nat_t *, nat, ftpinfo_t *, ftp,
699 		    int, dlen);
700 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
701 			printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
702 			       dlen);
703 		return 0;
704 	} else if (strncmp(f->ftps_rptr,
705 			   "227 Entering Passive Mod", PASV_REPLEN)) {
706 		DT2(ftp_PASV_error_string, nat_t *, nat, ftpinfo_t *, ftp);
707 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
708 			printf("ipf_p_ftp_pasv:%d reply wrong\n", 227);
709 		return 0;
710 	}
711 
712 	brackets[0] = "";
713 	brackets[1] = "";
714 	/*
715 	 * Skip the PASV reply + space
716 	 */
717 	s = f->ftps_rptr + PASV_REPLEN;
718 	while (*s && !ISDIGIT(*s)) {
719 		if (*s == '(') {
720 			brackets[0] = "(";
721 			brackets[1] = ")";
722 		}
723 		s++;
724 	}
725 
726 	/*
727 	 * Pick out the address components, two at a time.
728 	 */
729 	a1 = ipf_p_ftp_atoi(&s);
730 	if (s == NULL) {
731 		DT2(ftp_PASV_error_atoi_1, nat_t *, nat, ftpside_t *, f);
732 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
733 			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1);
734 		return 0;
735 	}
736 	a2 = ipf_p_ftp_atoi(&s);
737 	if (s == NULL) {
738 		DT2(ftp_PASV_error_atoi_2, nat_t *, nat, ftpside_t *, f);
739 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
740 			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2);
741 		return 0;
742 	}
743 
744 	/*
745 	 * check that IP address in the PASV reply is the same as the
746 	 * sender of the command - prevents using PASV for port scanning.
747 	 */
748 	a1 <<= 16;
749 	a1 |= a2;
750 
751 	if (((nat->nat_dir == NAT_INBOUND) &&
752 	     (a1 != ntohl(nat->nat_ndstaddr))) ||
753 	    ((nat->nat_dir == NAT_OUTBOUND) &&
754 	     (a1 != ntohl(nat->nat_odstaddr)))) {
755 		DT3(ftp_PASV_error_address, nat_t *, nat, ftpside_t *, f,
756 		    u_int, a1);
757 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
758 			printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1");
759 		return 0;
760 	}
761 
762 	a5 = ipf_p_ftp_atoi(&s);
763 	if (s == NULL) {
764 		DT2(ftp_PASV_error_atoi_3, nat_t *, nat, ftpside_t *, f);
765 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
766 			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3);
767 		return 0;
768 	}
769 
770 	if (*s == ')')
771 		s++;
772 	if (*s == '.')
773 		s++;
774 	if (*s == '\n')
775 		s--;
776 	/*
777 	 * check for CR-LF at the end.
778 	 */
779 	if ((*s != '\r') || (*(s + 1) != '\n')) {
780 		DT(pasv_missing_crlf);
781 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
782 			printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n");
783 		return 0;
784 	}
785 	s += 2;
786 
787 	a6 = a5 & 0xff;
788 	a5 >>= 8;
789 	/*
790 	 * Calculate new address parts for 227 reply
791 	 */
792 	if (nat->nat_dir == NAT_INBOUND) {
793 		data_ip = nat->nat_odstaddr;
794 		a1 = ntohl(data_ip);
795 	} else
796 		data_ip = htonl(a1);
797 
798 	a2 = (a1 >> 16) & 0xff;
799 	a3 = (a1 >> 8) & 0xff;
800 	a4 = a1 & 0xff;
801 	a1 >>= 24;
802 
803 	snprintf(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
804 		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
805 		a5, a6, brackets[1]);
806 	return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6),
807 				   newbuf, s);
808 }
809 
810 int
811 ipf_p_ftp_pasvreply(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip,
812 	nat_t *nat, ftpinfo_t *ftp, u_int port, char *newmsg, char *s)
813 {
814 	int inc, off, nflags;
815 	tcphdr_t *tcp, tcph, *tcp2;
816 	ipf_main_softc_t *softc;
817 	ipf_nat_softc_t *softn;
818 	size_t nlen, olen;
819 #ifdef USE_INET6
820 	ip6_t *ip6;
821 #endif
822 	ipnat_t *ipn;
823 	fr_info_t fi;
824 	ftpside_t *f;
825 	nat_t *nat2 = NULL;
826 	mb_t *m;
827 
828 	softc = fin->fin_main_soft;
829 	softn = softc->ipf_nat_soft;
830 
831 	if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL))
832 		ipf_p_ftp_setpending(softc, ftp);
833 
834 	m = fin->fin_m;
835 	tcp = (tcphdr_t *)fin->fin_dp;
836 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
837 
838 	tcp2 = &tcph;
839 	inc = 0;
840 
841 	f = &ftp->ftp_side[1];
842 	olen = s - f->ftps_rptr;
843 	nlen = strlen(newmsg);
844 	inc = nlen - olen;
845 	if ((inc + fin->fin_plen) > 65535) {
846 		DT3(ftp_PASV_error_inc, nat_t *, nat, ftpside_t *, f,
847 		    int, inc);
848 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
849 			printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
850 			       inc);
851 		return 0;
852 	}
853 
854 	ipn = ipf_proxy_rule_fwd(nat);
855 	if (ipn == NULL)
856 		return APR_ERR(1);
857 	ipn->in_use = 0;
858 
859 	/*
860 	 * Add skeleton NAT entry for connection which will come back the
861 	 * other way.
862 	 */
863 	bzero((char *)tcp2, sizeof(*tcp2));
864 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
865 	fi.fin_flx |= FI_IGNORE;
866 	fi.fin_data[0] = 0;
867 	fi.fin_data[1] = port;
868 	nflags = IPN_TCP|SI_W_SPORT;
869 
870 	fi.fin_fr = &ftppxyfr;
871 	fi.fin_dp = (char *)tcp2;
872 	fi.fin_out = 1 - fin->fin_out;
873 	fi.fin_dlen = sizeof(*tcp2);
874 	fi.fin_src6 = nat->nat_osrc6;
875 	fi.fin_dst6 = nat->nat_odst6;
876 	fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
877 	fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
878 
879 	TCP_OFF_A(tcp2, 5);
880 	tcp2->th_flags = TH_SYN;
881 	tcp2->th_win = htons(8192);
882 	tcp2->th_dport = htons(port);
883 
884 	MUTEX_ENTER(&softn->ipf_nat_new);
885 	if (nat->nat_v[0] == 6) {
886 #ifdef USE_INET6
887 		nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat,
888 				    nflags, nat->nat_dir);
889 #endif
890 	} else {
891 		nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat,
892 				   nflags, nat->nat_dir);
893 	}
894 	MUTEX_EXIT(&softn->ipf_nat_new);
895 
896 	if (nat2 == NULL) {
897 		KFREES(ipn, ipn->in_size);
898 		return APR_ERR(1);
899 	}
900 
901 	(void) ipf_nat_proto(&fi, nat2, IPN_TCP);
902 	MUTEX_ENTER(&nat2->nat_lock);
903 	ipf_nat_update(&fi, nat2);
904 	MUTEX_EXIT(&nat2->nat_lock);
905 	fi.fin_ifp = NULL;
906 	if (nat->nat_dir == NAT_INBOUND) {
907 		if (nat->nat_v[0] == 6) {
908 #ifdef USE_INET6
909 			fi.fin_dst6 = nat->nat_ndst6;
910 #endif
911 		} else {
912 			fi.fin_daddr = nat->nat_ndstaddr;
913 		}
914 	}
915 	if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate,
916 			  SI_W_SPORT) != 0)
917 		ipf_nat_setpending(softc, nat2);
918 
919 #if !defined(_KERNEL)
920 	M_ADJ(m, inc);
921 #else
922 	/*
923 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
924 	 * mean remove -len bytes from the end of the packet.
925 	 * The mbuf chain will be extended if necessary by m_copyback().
926 	 */
927 	if (inc < 0)
928 		M_ADJ(m, inc);
929 #endif /* !defined(_KERNEL) */
930 	COPYBACK(m, off, nlen, newmsg);
931 	fin->fin_flx |= FI_DOCKSUM;
932 
933 	if (inc != 0) {
934 		fin->fin_plen += inc;
935 		fin->fin_dlen += inc;
936 		if (nat->nat_v[0] == 6) {
937 #ifdef USE_INET6
938 			ip6 = (ip6_t *)fin->fin_ip;
939 			u_short len = ntohs(ip6->ip6_plen) + inc;
940 			ip6->ip6_plen = htons(len);
941 #endif
942 		} else {
943 			ip->ip_len = htons(fin->fin_plen);
944 		}
945 	}
946 
947 	return APR_INC(inc);
948 }
949 
950 
951 int
952 ipf_p_ftp_server(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
953     ftpinfo_t *ftp, int dlen)
954 {
955 	char *rptr, *wptr;
956 	ftpside_t *f;
957 	int inc;
958 
959 	inc = 0;
960 	f = &ftp->ftp_side[1];
961 	rptr = f->ftps_rptr;
962 	wptr = f->ftps_wptr;
963 
964 	DT2(ftp_server_response, char *, rptr, int, ftp->ftp_passok);
965 	if (*rptr == ' ')
966 		goto server_cmd_ok;
967 	if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
968 		return 0;
969 	if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
970 		printf("ipf_p_ftp_server_1: cmd[%4.4s] passok %d\n",
971 		       rptr, ftp->ftp_passok);
972 	if (ftp->ftp_passok == FTPXY_GO) {
973 		if (!strncmp(rptr, "227 ", 4))
974 			inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
975 		else if (!strncmp(rptr, "229 ", 4))
976 			inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
977 		else if (strncmp(rptr, "200", 3)) {
978 			/*
979 			 * 200 is returned for a successful command.
980 			 */
981 			;
982 		}
983 	} else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
984 		inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
985 	} else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
986 		inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
987 	} else if (*rptr == '5' || *rptr == '4')
988 		ftp->ftp_passok = FTPXY_INIT;
989 	else if (ftp->ftp_incok) {
990 		if (*rptr == '3') {
991 			if (ftp->ftp_passok == FTPXY_ACCT_1)
992 				ftp->ftp_passok = FTPXY_GO;
993 			else
994 				ftp->ftp_passok++;
995 		} else if (*rptr == '2') {
996 			switch (ftp->ftp_passok)
997 			{
998 			case FTPXY_USER_1 :
999 			case FTPXY_USER_2 :
1000 			case FTPXY_PASS_1 :
1001 			case FTPXY_PASS_2 :
1002 			case FTPXY_ACCT_1 :
1003 				ftp->ftp_passok = FTPXY_GO;
1004 				break;
1005 			default :
1006 				ftp->ftp_passok += 3;
1007 				break;
1008 			}
1009 		}
1010 	}
1011 	ftp->ftp_incok = 0;
1012 server_cmd_ok:
1013 	if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1014 		printf("ipf_p_ftp_server_2: cmd[%4.4s] passok %d\n",
1015 		       rptr, ftp->ftp_passok);
1016 	DT3(ftp_server_passok, char *,rptr, int, ftp->ftp_incok,
1017 	    int, ftp->ftp_passok);
1018 
1019 	while ((*rptr++ != '\n') && (rptr < wptr))
1020 		;
1021 	f->ftps_rptr = rptr;
1022 	return inc;
1023 }
1024 
1025 
1026 /*
1027  * 0 FTPXY_JUNK_OK
1028  * 1 FTPXY_JUNK_BAD
1029  * 2 FTPXY_JUNK_EOL
1030  * 3 FTPXY_JUNK_CONT
1031  *
1032  * Look to see if the buffer starts with something which we recognise as
1033  * being the correct syntax for the FTP protocol.
1034  */
1035 int
1036 ipf_p_ftp_client_valid(ipf_ftp_softc_t *softf, ftpside_t *ftps, char *buf,
1037     size_t len)
1038 {
1039 	register char *s, c, pc;
1040 	register size_t i = len;
1041 	char cmd[5];
1042 
1043 	s = buf;
1044 
1045 	if (ftps->ftps_junk == FTPXY_JUNK_BAD)
1046 		return FTPXY_JUNK_BAD;
1047 
1048 	if (i < 5) {
1049 		DT1(client_valid, int, i);
1050 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1051 			printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i);
1052 		return 2;
1053 	}
1054 
1055 	i--;
1056 	c = *s++;
1057 
1058 	if (ISALPHA(c)) {
1059 		cmd[0] = TOUPPER(c);
1060 		c = *s++;
1061 		i--;
1062 		if (ISALPHA(c)) {
1063 			cmd[1] = TOUPPER(c);
1064 			c = *s++;
1065 			i--;
1066 			if (ISALPHA(c)) {
1067 				cmd[2] = TOUPPER(c);
1068 				c = *s++;
1069 				i--;
1070 				if (ISALPHA(c)) {
1071 					cmd[3] = TOUPPER(c);
1072 					c = *s++;
1073 					i--;
1074 					if ((c != ' ') && (c != '\r'))
1075 						goto bad_client_command;
1076 				} else if ((c != ' ') && (c != '\r'))
1077 					goto bad_client_command;
1078 			} else
1079 				goto bad_client_command;
1080 		} else
1081 			goto bad_client_command;
1082 	} else {
1083 bad_client_command:
1084 		DT4(client_junk, int, len, int, i, int, c, char *, buf);
1085 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1086 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
1087 			       "ipf_p_ftp_client_valid",
1088 			       ftps->ftps_junk, (int)len, (int)i, c,
1089 			       (int)len, (int)len, buf);
1090 		return FTPXY_JUNK_BAD;
1091 	}
1092 
1093 	for (; i; i--) {
1094 		pc = c;
1095 		c = *s++;
1096 		if ((pc == '\r') && (c == '\n')) {
1097 			cmd[4] = '\0';
1098 			if (!strcmp(cmd, "PASV")) {
1099 				ftps->ftps_cmd = FTPXY_C_PASV;
1100 			} else if (!strcmp(cmd, "EPSV")) {
1101 				ftps->ftps_cmd = FTPXY_C_EPSV;
1102 			} else {
1103 				ftps->ftps_cmd = 0;
1104 			}
1105 			return 0;
1106 		}
1107 	}
1108 #if !defined(_KERNEL)
1109 	printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n",
1110 	       (int)len, (int)len, buf);
1111 #endif
1112 	return FTPXY_JUNK_EOL;
1113 }
1114 
1115 
1116 int
1117 ipf_p_ftp_server_valid(ipf_ftp_softc_t *softf, ftpside_t *ftps, char *buf,
1118     size_t len)
1119 {
1120 	register char *s, c, pc;
1121 	register size_t i = len;
1122 	int cmd;
1123 
1124 	s = buf;
1125 	cmd = 0;
1126 
1127 	if (ftps->ftps_junk == FTPXY_JUNK_BAD)
1128 		return FTPXY_JUNK_BAD;
1129 
1130 	if (i < 5) {
1131 		DT1(server_valid, int, i);
1132 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1133 			printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i);
1134 		return 2;
1135 	}
1136 
1137 	c = *s++;
1138 	i--;
1139 	if (c == ' ') {
1140 		cmd = -1;
1141 		goto search_eol;
1142 	}
1143 
1144 	if (ISDIGIT(c)) {
1145 		cmd = (c - '0') * 100;
1146 		c = *s++;
1147 		i--;
1148 		if (ISDIGIT(c)) {
1149 			cmd += (c - '0') * 10;
1150 			c = *s++;
1151 			i--;
1152 			if (ISDIGIT(c)) {
1153 				cmd += (c - '0');
1154 				c = *s++;
1155 				i--;
1156 				if ((c != '-') && (c != ' '))
1157 					goto bad_server_command;
1158 				if (c == '-')
1159 					return FTPXY_JUNK_CONT;
1160 			} else
1161 				goto bad_server_command;
1162 		} else
1163 			goto bad_server_command;
1164 	} else {
1165 bad_server_command:
1166 		DT4(server_junk, int len, buf, int, i, int, c, char *, buf);
1167 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO)
1168 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
1169 			       "ipf_p_ftp_server_valid",
1170 			       ftps->ftps_junk, (int)len, (int)i,
1171 			       c, (int)len, (int)len, buf);
1172 		if (ftps->ftps_junk == FTPXY_JUNK_CONT)
1173 			return FTPXY_JUNK_CONT;
1174 		return FTPXY_JUNK_BAD;
1175 	}
1176 search_eol:
1177 	for (; i; i--) {
1178 		pc = c;
1179 		c = *s++;
1180 		if ((pc == '\r') && (c == '\n')) {
1181 			if (cmd == -1) {
1182 				if (ftps->ftps_junk == FTPXY_JUNK_CONT)
1183 					return FTPXY_JUNK_CONT;
1184 			} else {
1185 				ftps->ftps_cmd = cmd;
1186 			}
1187 			return FTPXY_JUNK_OK;
1188 		}
1189 	}
1190 
1191 	DT2(junk_eol, int, len, char *, buf);
1192 	if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO)
1193 		printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n",
1194 		       (int)len, (int)len, buf);
1195 	return FTPXY_JUNK_EOL;
1196 }
1197 
1198 
1199 int
1200 ipf_p_ftp_valid(ipf_ftp_softc_t *softf, ftpinfo_t *ftp, int side, char *buf,
1201     size_t len)
1202 {
1203 	ftpside_t *ftps;
1204 	int ret;
1205 
1206 	ftps = &ftp->ftp_side[side];
1207 
1208 	if (side == 0)
1209 		ret = ipf_p_ftp_client_valid(softf, ftps, buf, len);
1210 	else
1211 		ret = ipf_p_ftp_server_valid(softf, ftps, buf, len);
1212 	return ret;
1213 }
1214 
1215 
1216 /*
1217  * For map rules, the following applies:
1218  * rv == 0 for outbound processing,
1219  * rv == 1 for inbound processing.
1220  * For rdr rules, the following applies:
1221  * rv == 0 for inbound processing,
1222  * rv == 1 for outbound processing.
1223  */
1224 int
1225 ipf_p_ftp_process(ipf_ftp_softc_t *softf, fr_info_t *fin, nat_t *nat,
1226     ftpinfo_t *ftp, int rv)
1227 {
1228 	int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry;
1229 	char *rptr, *wptr, *s;
1230 	u_32_t thseq, thack;
1231 	ap_session_t *aps;
1232 	ftpside_t *f, *t;
1233 	tcphdr_t *tcp;
1234 	ip_t *ip;
1235 	mb_t *m;
1236 
1237 	m = fin->fin_m;
1238 	ip = fin->fin_ip;
1239 	tcp = (tcphdr_t *)fin->fin_dp;
1240 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1241 
1242 	f = &ftp->ftp_side[rv];
1243 	t = &ftp->ftp_side[1 - rv];
1244 	thseq = ntohl(tcp->th_seq);
1245 	thack = ntohl(tcp->th_ack);
1246 #ifdef __sgi
1247 	mlen = fin->fin_plen - off;
1248 #else
1249 	mlen = MSGDSIZE(m) - off;
1250 #endif
1251 
1252 	DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen);
1253 	if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1254 		printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n",
1255 		       fin->fin_out, fin->fin_sport, fin->fin_dport,
1256 		       mlen, tcp->th_flags);
1257 
1258 	if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) {
1259 		f->ftps_seq[0] = thseq + 1;
1260 		t->ftps_seq[0] = thack;
1261 		return 0;
1262 	} else if (mlen < 0) {
1263 		return 0;
1264 	}
1265 
1266 	aps = nat->nat_aps;
1267 
1268 	sel = aps->aps_sel[1 - rv];
1269 	sel2 = aps->aps_sel[rv];
1270 	if (rv == 1) {
1271 		seqoff = aps->aps_seqoff[sel];
1272 		if (aps->aps_seqmin[sel] > seqoff + thseq)
1273 			seqoff = aps->aps_seqoff[!sel];
1274 		ackoff = aps->aps_ackoff[sel2];
1275 		if (aps->aps_ackmin[sel2] > ackoff + thack)
1276 			ackoff = aps->aps_ackoff[!sel2];
1277 	} else {
1278 		seqoff = aps->aps_ackoff[sel];
1279 		if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1280 			printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1281 			       aps->aps_ackmin[sel]);
1282 		if (aps->aps_ackmin[sel] > seqoff + thseq)
1283 			seqoff = aps->aps_ackoff[!sel];
1284 
1285 		ackoff = aps->aps_seqoff[sel2];
1286 		if (softf->ipf_p_ftp_debug & DEBUG_INFO)
1287 			printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1288 			       aps->aps_seqmin[sel2]);
1289 		if (ackoff > 0) {
1290 			if (aps->aps_seqmin[sel2] > ackoff + thack)
1291 				ackoff = aps->aps_seqoff[!sel2];
1292 		} else {
1293 			if (aps->aps_seqmin[sel2] > thack)
1294 				ackoff = aps->aps_seqoff[!sel2];
1295 		}
1296 	}
1297 	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1298 		printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1299 		       rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1300 		       thack, ackoff, mlen, fin->fin_plen, off);
1301 		printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1302 		       aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1303 		       aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1304 		printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1305 		       aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1306 		       aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1307 	}
1308 
1309 	/*
1310 	 * XXX - Ideally, this packet should get dropped because we now know
1311 	 * that it is out of order (and there is no real danger in doing so
1312 	 * apart from causing packets to go through here ordered).
1313 	 */
1314 	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1315 		printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1316 		       rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1317 	}
1318 
1319 	ok = 0;
1320 	if (t->ftps_seq[0] == 0) {
1321 		t->ftps_seq[0] = thack;
1322 		ok = 1;
1323 	} else {
1324 		if (ackoff == 0) {
1325 			if (t->ftps_seq[0] == thack)
1326 				ok = 1;
1327 			else if (t->ftps_seq[1] == thack) {
1328 				t->ftps_seq[0] = thack;
1329 				ok = 1;
1330 			}
1331 		} else {
1332 			if (t->ftps_seq[0] + ackoff == thack) {
1333 				t->ftps_seq[0] = thack;
1334 				ok = 1;
1335 			} else if (t->ftps_seq[0] == thack + ackoff) {
1336 				t->ftps_seq[0] = thack + ackoff;
1337 				ok = 1;
1338 			} else if (t->ftps_seq[1] + ackoff == thack) {
1339 				t->ftps_seq[0] = thack;
1340 				ok = 1;
1341 			} else if (t->ftps_seq[1] == thack + ackoff) {
1342 				t->ftps_seq[0] = thack + ackoff;
1343 				ok = 1;
1344 			}
1345 		}
1346 	}
1347 
1348 	if (softf->ipf_p_ftp_debug & DEBUG_INFO) {
1349 		if (!ok)
1350 			printf("%s ok\n", "not");
1351 	}
1352 
1353 	if (!mlen) {
1354 		if (t->ftps_seq[0] + ackoff != thack &&
1355 		    t->ftps_seq[1] + ackoff != thack) {
1356 			DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack);
1357 			if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1358 				printf("%s:seq[0](%u) + (%d) != (%u)\n",
1359 				       "ipf_p_ftp_process", t->ftps_seq[0],
1360 				       ackoff, thack);
1361 				printf("%s:seq[0](%u) + (%d) != (%u)\n",
1362 				       "ipf_p_ftp_process", t->ftps_seq[1],
1363 				       ackoff, thack);
1364 			}
1365 			return APR_ERR(1);
1366 		}
1367 
1368 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE) {
1369 			printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n",
1370 				f->ftps_seq[0], f->ftps_seq[1]);
1371 		}
1372 
1373 		if (tcp->th_flags & TH_FIN) {
1374 			if (thseq == f->ftps_seq[1]) {
1375 				f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1376 				f->ftps_seq[1] = thseq + 1 - seqoff;
1377 			} else {
1378 				DT2(thseq, ftpside_t *t, t, u_32_t, thseq);
1379 				if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1380 					printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1381 					       thseq, seqoff, f->ftps_seq[0]);
1382 				}
1383 				return APR_ERR(1);
1384 			}
1385 		}
1386 		f->ftps_len = 0;
1387 		return 0;
1388 	}
1389 
1390 	ok = 0;
1391 	if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1392 		ok = 1;
1393 	/*
1394 	 * Retransmitted data packet.
1395 	 */
1396 	} else if ((thseq + mlen == f->ftps_seq[0]) ||
1397 		   (thseq + mlen == f->ftps_seq[1])) {
1398 		ok = 1;
1399 	}
1400 
1401 	if (ok == 0) {
1402 		DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen);
1403 		inc = thseq - f->ftps_seq[0];
1404 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR) {
1405 			printf("inc %d sel %d rv %d\n", inc, sel, rv);
1406 			printf("th_seq %x ftps_seq %x/%x\n",
1407 			       thseq, f->ftps_seq[0], f->ftps_seq[1]);
1408 			printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1409 			       aps->aps_ackoff[sel]);
1410 			printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1411 			       aps->aps_seqoff[sel]);
1412 		}
1413 
1414 		return APR_ERR(1);
1415 	}
1416 
1417 	inc = 0;
1418 	rptr = f->ftps_rptr;
1419 	wptr = f->ftps_wptr;
1420 	f->ftps_seq[0] = thseq;
1421 	f->ftps_seq[1] = f->ftps_seq[0] + mlen;
1422 	f->ftps_len = mlen;
1423 
1424 	while (mlen > 0) {
1425 		len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1426 		if (len == 0)
1427 			break;
1428 		COPYDATA(m, off, len, wptr);
1429 		mlen -= len;
1430 		off += len;
1431 		wptr += len;
1432 
1433 whilemore:
1434 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1435 			printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
1436 			       "ipf_p_ftp_process",
1437 			       len, mlen, off, (u_long)wptr, f->ftps_junk,
1438 			       len, len, rptr);
1439 
1440 		f->ftps_wptr = wptr;
1441 		if (f->ftps_junk != FTPXY_JUNK_OK) {
1442 			i = f->ftps_junk;
1443 			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr,
1444 						      wptr - rptr);
1445 			DT2(junk_transit, int, i, int, f->ftps_junk);
1446 
1447 			if (softf->ipf_p_ftp_debug & DEBUG_PARSE)
1448 				printf("%s:junk %d -> %d\n",
1449 				       "ipf_p_ftp_process", i, f->ftps_junk);
1450 
1451 			if (f->ftps_junk == FTPXY_JUNK_BAD) {
1452 				DT(buffer_full);
1453 				if (wptr - rptr == sizeof(f->ftps_buf)) {
1454 					if (softf->ipf_p_ftp_debug &
1455 					    DEBUG_PARSE_INFO)
1456 						printf("%s:full buffer\n",
1457 						       "ipf_p_ftp_process");
1458 					f->ftps_rptr = f->ftps_buf;
1459 					f->ftps_wptr = f->ftps_buf;
1460 					rptr = f->ftps_rptr;
1461 					wptr = f->ftps_wptr;
1462 					continue;
1463 				}
1464 			}
1465 		}
1466 
1467 		while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) {
1468 			len = wptr - rptr;
1469 			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv,
1470 						       rptr, len);
1471 
1472 			if (softf->ipf_p_ftp_debug & DEBUG_PARSE) {
1473 				printf("%s=%d len %d rv %d ptr %lx/%lx ",
1474 				       "ipf_p_ftp_valid",
1475 				       f->ftps_junk, len, rv, (u_long)rptr,
1476 				       (u_long)wptr);
1477 				printf("buf [%*.*s]\n", len, len, rptr);
1478 			}
1479 
1480 			if (f->ftps_junk == FTPXY_JUNK_OK) {
1481 				f->ftps_cmds++;
1482 				f->ftps_rptr = rptr;
1483 				if (rv)
1484 					inc += ipf_p_ftp_server(softf, fin, ip,
1485 								nat, ftp, len);
1486 				else
1487 					inc += ipf_p_ftp_client(softf, fin, ip,
1488 								nat, ftp, len);
1489 				rptr = f->ftps_rptr;
1490 				wptr = f->ftps_wptr;
1491 			}
1492 		}
1493 
1494 		/*
1495 		 * Off to a bad start so lets just forget about using the
1496 		 * ftp proxy for this connection.
1497 		 */
1498 		if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) {
1499 			/* f->ftps_seq[1] += inc; */
1500 
1501 			DT(ftp_junk_cmd);
1502 			if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1503 				printf("%s:cmds == 0 junk == 1\n",
1504 				       "ipf_p_ftp_process");
1505 			return APR_ERR(2);
1506 		}
1507 
1508 		retry = 0;
1509 		if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) {
1510 			for (s = rptr; s < wptr; s++) {
1511 				if ((*s == '\r') && (s + 1 < wptr) &&
1512 				    (*(s + 1) == '\n')) {
1513 					rptr = s + 2;
1514 					retry = 1;
1515 					if (f->ftps_junk != FTPXY_JUNK_CONT)
1516 						f->ftps_junk = FTPXY_JUNK_OK;
1517 					break;
1518 				}
1519 			}
1520 		}
1521 
1522 		if (rptr == wptr) {
1523 			rptr = wptr = f->ftps_buf;
1524 		} else {
1525 			/*
1526 			 * Compact the buffer back to the start.  The junk
1527 			 * flag should already be set and because we're not
1528 			 * throwing away any data, it is preserved from its
1529 			 * current state.
1530 			 */
1531 			if (rptr > f->ftps_buf) {
1532 				bcopy(rptr, f->ftps_buf, wptr - rptr);
1533 				wptr -= rptr - f->ftps_buf;
1534 				rptr = f->ftps_buf;
1535 			}
1536 		}
1537 		f->ftps_rptr = rptr;
1538 		f->ftps_wptr = wptr;
1539 		if (retry)
1540 			goto whilemore;
1541 	}
1542 
1543 	/* f->ftps_seq[1] += inc; */
1544 	if (tcp->th_flags & TH_FIN)
1545 		f->ftps_seq[1]++;
1546 	if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) {
1547 #ifdef __sgi
1548 		mlen = fin->fin_plen;
1549 #else
1550 		mlen = MSGDSIZE(m);
1551 #endif
1552 		mlen -= off;
1553 		printf("ftps_seq[1] = %x inc %d len %d\n",
1554 		       f->ftps_seq[1], inc, mlen);
1555 	}
1556 
1557 	f->ftps_rptr = rptr;
1558 	f->ftps_wptr = wptr;
1559 	return APR_INC(inc);
1560 }
1561 
1562 
1563 int
1564 ipf_p_ftp_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
1565 {
1566 	ipf_ftp_softc_t *softf = arg;
1567 	ftpinfo_t *ftp;
1568 	int rev;
1569 
1570 	ftp = aps->aps_data;
1571 	if (ftp == NULL)
1572 		return 0;
1573 
1574 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1575 	if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1576 		ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1577 
1578 	return ipf_p_ftp_process(softf, fin, nat, ftp, rev);
1579 }
1580 
1581 
1582 int
1583 ipf_p_ftp_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
1584 {
1585 	ipf_ftp_softc_t *softf = arg;
1586 	ftpinfo_t *ftp;
1587 	int rev;
1588 
1589 	ftp = aps->aps_data;
1590 	if (ftp == NULL)
1591 		return 0;
1592 
1593 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1594 	if (ftp->ftp_side[rev].ftps_ifp == NULL)
1595 		ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1596 
1597 	return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev);
1598 }
1599 
1600 
1601 /*
1602  * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in
1603  * pairs separated by commas (which are expected to be in the range 0 - 255),
1604  * returning a 16 bit number combining either side of the , as the MSB and
1605  * LSB.
1606  */
1607 u_short
1608 ipf_p_ftp_atoi(char **ptr)
1609 {
1610 	register char *s = *ptr, c;
1611 	register u_char i = 0, j = 0;
1612 
1613 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1614 		i *= 10;
1615 		i += c - '0';
1616 	}
1617 	if (c != ',') {
1618 		*ptr = NULL;
1619 		return 0;
1620 	}
1621 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1622 		j *= 10;
1623 		j += c - '0';
1624 	}
1625 	*ptr = s;
1626 	i &= 0xff;
1627 	j &= 0xff;
1628 	return (i << 8) | j;
1629 }
1630 
1631 
1632 int
1633 ipf_p_ftp_eprt(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
1634     ftpinfo_t *ftp, int dlen)
1635 {
1636 	ftpside_t *f;
1637 
1638 	/*
1639 	 * Check for client sending out EPRT message.
1640 	 */
1641 	if (dlen < IPF_MINEPRTLEN) {
1642 		DT1(epert_dlen, int, dlen);
1643 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1644 			printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n",
1645 				dlen);
1646 		return 0;
1647 	}
1648 
1649 	/*
1650 	 * Parse the EPRT command.  Format is:
1651 	 * "EPRT |1|1.2.3.4|2000|" for IPv4 and
1652 	 * "EPRT |2|ef00::1:2|2000|" for IPv6
1653 	 */
1654 	f = &ftp->ftp_side[0];
1655 	if (f->ftps_rptr[5] != '|')
1656 		return 0;
1657 	if (f->ftps_rptr[5] == f->ftps_rptr[7]) {
1658 		if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4)
1659 			return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen);
1660 #ifdef USE_INET6
1661 		if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6)
1662 			return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen);
1663 #endif
1664 	}
1665 	return 0;
1666 }
1667 
1668 
1669 int
1670 ipf_p_ftp_eprt4(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
1671     ftpinfo_t *ftp, int dlen)
1672 {
1673 	int a1, a2, a3, a4, port, olen, nlen, inc, off;
1674 	char newbuf[IPF_FTPBUFSZ];
1675 	char *s, c, delim;
1676 	u_32_t addr, i;
1677 	tcphdr_t *tcp;
1678 	ftpside_t *f;
1679 	mb_t *m;
1680 
1681 	m = fin->fin_m;
1682 	tcp = (tcphdr_t *)fin->fin_dp;
1683 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1684 	f = &ftp->ftp_side[0];
1685 	delim = f->ftps_rptr[5];
1686 	s = f->ftps_rptr + 8;
1687 
1688 	/*
1689 	 * get the IP address.
1690 	 */
1691 	i = 0;
1692 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1693 		i *= 10;
1694 		i += c - '0';
1695 	}
1696 	if (i > 255)
1697 		return 0;
1698 	if (c != '.')
1699 		return 0;
1700 	addr = (i << 24);
1701 
1702 	i = 0;
1703 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1704 		i *= 10;
1705 		i += c - '0';
1706 	}
1707 	if (i > 255)
1708 		return 0;
1709 	if (c != '.')
1710 		return 0;
1711 	addr |= (addr << 16);
1712 
1713 	i = 0;
1714 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1715 		i *= 10;
1716 		i += c - '0';
1717 	}
1718 	if (i > 255)
1719 		return 0;
1720 	if (c != '.')
1721 		return 0;
1722 	addr |= (addr << 8);
1723 
1724 	i = 0;
1725 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1726 		i *= 10;
1727 		i += c - '0';
1728 	}
1729 	if (i > 255)
1730 		return 0;
1731 	if (c != delim)
1732 		return 0;
1733 	addr |= addr;
1734 
1735 	/*
1736 	 * Get the port number
1737 	 */
1738 	i = 0;
1739 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1740 		i *= 10;
1741 		i += c - '0';
1742 	}
1743 	if (i > 65535)
1744 		return 0;
1745 	if (c != delim)
1746 		return 0;
1747 	port = i;
1748 
1749 	/*
1750 	 * Check for CR-LF at the end of the command string.
1751 	 */
1752 	if ((*s != '\r') || (*(s + 1) != '\n')) {
1753 		DT(eprt4_no_crlf);
1754 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1755 			printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf");
1756 		return 0;
1757 	}
1758 	s += 2;
1759 
1760 	/*
1761 	 * Calculate new address parts for PORT command
1762 	 */
1763 	if (nat->nat_dir == NAT_INBOUND)
1764 		a1 = ntohl(nat->nat_odstaddr);
1765 	else
1766 		a1 = ntohl(ip->ip_src.s_addr);
1767 	a2 = (a1 >> 16) & 0xff;
1768 	a3 = (a1 >> 8) & 0xff;
1769 	a4 = a1 & 0xff;
1770 	a1 >>= 24;
1771 	olen = s - f->ftps_rptr;
1772 	/*
1773 	 * While we could force the use of | as a delimiter here, it makes
1774 	 * sense to preserve whatever character is being used by the systems
1775 	 * involved in the communication.
1776 	 */
1777 	snprintf(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
1778 		 "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim);
1779 
1780 	nlen = strlen(newbuf);
1781 	inc = nlen - olen;
1782 	if ((inc + fin->fin_plen) > 65535) {
1783 		DT2(eprt4_len, int, inc, int, fin->fin_plen);
1784 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
1785 			printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n",
1786 				inc);
1787 		return 0;
1788 	}
1789 
1790 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1791 #if !defined(_KERNEL)
1792 	M_ADJ(m, inc);
1793 #else
1794 	if (inc < 0)
1795 		M_ADJ(m, inc);
1796 #endif
1797 	/* the mbuf chain will be extended if necessary by m_copyback() */
1798 	COPYBACK(m, off, nlen, newbuf);
1799 	fin->fin_flx |= FI_DOCKSUM;
1800 
1801 	if (inc != 0) {
1802 		fin->fin_plen += inc;
1803 		ip->ip_len = htons(fin->fin_plen);
1804 		fin->fin_dlen += inc;
1805 	}
1806 
1807 	f->ftps_cmd = FTPXY_C_EPRT;
1808 	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
1809 }
1810 
1811 
1812 int
1813 ipf_p_ftp_epsv(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
1814     ftpinfo_t *ftp, int dlen)
1815 {
1816 	char newbuf[IPF_FTPBUFSZ];
1817 	u_short ap = 0;
1818 	ftpside_t *f;
1819 	char *s;
1820 
1821 	if ((softf->ipf_p_ftp_forcepasv != 0) &&
1822 	    (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) {
1823 		DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd);
1824 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1825 			printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n",
1826 			       ftp->ftp_side[0].ftps_cmd);
1827 		return 0;
1828 	}
1829 	f = &ftp->ftp_side[1];
1830 
1831 #define EPSV_REPLEN	33
1832 	/*
1833 	 * Check for EPSV reply message.
1834 	 */
1835 	if (dlen < IPF_MIN229LEN) {
1836 		return (0);
1837 	} else if (strncmp(f->ftps_rptr,
1838 			 "229 Entering Extended Passive Mode", EPSV_REPLEN)) {
1839 		return (0);
1840 }
1841 
1842 	/*
1843 	 * Skip the EPSV command + space
1844 	 */
1845 	s = f->ftps_rptr + 33;
1846 	while (*s && !ISDIGIT(*s))
1847 		s++;
1848 
1849 	/*
1850 	 * As per RFC 2428, there are no addres components in the EPSV
1851 	 * response.  So we'll go straight to getting the port.
1852 	 */
1853 	while (*s && ISDIGIT(*s)) {
1854 		ap *= 10;
1855 		ap += *s++ - '0';
1856 	}
1857 
1858 	if (!s) {
1859 		return 0;
1860 }
1861 
1862 	if (*s == '|')
1863 		s++;
1864 	if (*s == ')')
1865 		s++;
1866 	if (*s == '\n')
1867 		s--;
1868 	/*
1869 	 * check for CR-LF at the end.
1870 	 */
1871 	if ((*s != '\r') || (*(s + 1) != '\n')) {
1872 		return 0;
1873 	}
1874 	s += 2;
1875 
1876 	snprintf(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1877 		 "229 Entering Extended Passive Mode", ap);
1878 
1879 	return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap,
1880 				   newbuf, s);
1881 }
1882 
1883 #ifdef USE_INET6
1884 int
1885 ipf_p_ftp_eprt6(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip,
1886 	nat_t *nat, ftpinfo_t *ftp, int dlen)
1887 {
1888 	int port, olen, nlen, inc, off, left, i;
1889 	char newbuf[IPF_FTPBUFSZ];
1890 	char *s, c;
1891 	i6addr_t addr, *a6;
1892 	tcphdr_t *tcp;
1893 	ip6_t *ip6;
1894 	char delim;
1895 	u_short whole;
1896 	u_short part;
1897 	ftpside_t *f;
1898 	u_short *t;
1899 	int fwd;
1900 	mb_t *m;
1901 	u_32_t a;
1902 
1903 	m = fin->fin_m;
1904 	ip6 = (ip6_t *)ip;
1905 	f = &ftp->ftp_side[0];
1906 	s = f->ftps_rptr + 8;
1907 	f = &ftp->ftp_side[0];
1908 	delim = f->ftps_rptr[5];
1909 	tcp = (tcphdr_t *)fin->fin_dp;
1910 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1911 
1912 	addr.i6[0] = 0;
1913 	addr.i6[1] = 0;
1914 	addr.i6[2] = 0;
1915 	addr.i6[3] = 0;
1916 	/*
1917 	 * Parse an IPv6 address.
1918 	 * Go forward until either :: or | is found. If :: is found,
1919 	 * reverse direction. Direction change is performed to ease
1920 	 * parsing an unknown number of 0s in the middle.
1921 	 */
1922 	whole = 0;
1923 	t = (u_short *)&addr;
1924 	fwd = 1;
1925 	for (part = 0; (c = *s) != '\0'; ) {
1926 		if (c == delim) {
1927 			*t = htons((u_short)whole);
1928 			break;
1929 		}
1930 		if (c == ':') {
1931 			*t = part;
1932 			if (fwd) {
1933 				*t = htons((u_short)whole);
1934 				t++;
1935 			} else {
1936 				*t = htons((u_short)(whole >> 16));
1937 				t--;
1938 			}
1939 			whole = 0;
1940 			if (fwd == 1 && s[1] == ':') {
1941 				while (*s && *s != '|')
1942 					s++;
1943 				if ((c = *s) != delim)
1944 					break;
1945 				t = (u_short *)&addr.i6[3];
1946 				t++;
1947 				fwd = 0;
1948 			} else if (fwd == 0 && s[-1] == ':') {
1949 				break;
1950 			}
1951 		} else {
1952 			if (c >= '0' && c <= '9') {
1953 				c -= '0';
1954 			} else if (c >= 'a' && c <= 'f') {
1955 				c -= 'a' + 10;
1956 			} else if (c >= 'A' && c <= 'F') {
1957 				c -= 'A' + 10;
1958 			}
1959 			if (fwd) {
1960 				whole <<= 8;
1961 				whole |= c;
1962 			} else {
1963 				whole >>= 8;
1964 				whole |= ((u_32_t)c) << 24;
1965 			}
1966 		}
1967 		if (fwd)
1968 			s++;
1969 		else
1970 			s--;
1971 	}
1972 	if (c != ':' && c != delim)
1973 		return 0;
1974 
1975 	while (*s != '|')
1976 		s++;
1977 	s++;
1978 
1979 	/*
1980 	 * Get the port number
1981 	 */
1982 	i = 0;
1983 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1984 		i *= 10;
1985 		i += c - '0';
1986 	}
1987 	if (i > 65535)
1988 		return 0;
1989 	if (c != delim)
1990 		return 0;
1991 	port = (u_short)(i & 0xffff);
1992 
1993 	/*
1994 	 * Check for CR-LF at the end of the command string.
1995 	 */
1996 	if ((*s != '\r') || (*(s + 1) != '\n')) {
1997 		DT(eprt6_no_crlf);
1998 		if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR)
1999 			printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf");
2000 		return 0;
2001 	}
2002 	s += 2;
2003 
2004 	/*
2005 	 * Calculate new address parts for PORT command
2006 	 */
2007 	a6 = (i6addr_t *)&ip6->ip6_src;
2008 	olen = s - f->ftps_rptr;
2009 	/* DO NOT change this to snprintf! */
2010 	/*
2011 	 * While we could force the use of | as a delimiter here, it makes
2012 	 * sense to preserve whatever character is being used by the systems
2013 	 * involved in the communication.
2014 	 */
2015 	s = newbuf;
2016 	left = sizeof(newbuf);
2017 	snprintf(newbuf, left, "EPRT %c2%c", delim, delim);
2018 	left -= strlen(s) + 1;
2019 	s += strlen(s);
2020 	a = ntohl(a6->i6[0]);
2021 	snprintf(s, left, "%x:%x:", a >> 16, a & 0xffff);
2022 	left -= strlen(s);
2023 	s += strlen(s);
2024 	a = ntohl(a6->i6[1]);
2025 	snprintf(s, left, "%x:%x:", a >> 16, a & 0xffff);
2026 	left -= strlen(s);
2027 	s += strlen(s);
2028 	a = ntohl(a6->i6[2]);
2029 	snprintf(s, left, "%x:%x:", a >> 16, a & 0xffff);
2030 	left -= strlen(s);
2031 	s += strlen(s);
2032 	a = ntohl(a6->i6[3]);
2033 	snprintf(s, left, "%x:%x", a >> 16, a & 0xffff);
2034 	left -= strlen(s);
2035 	s += strlen(s);
2036 	snprintf(s, left, "|%d|\r\n", port);
2037 	nlen = strlen(newbuf);
2038 	inc = nlen - olen;
2039 	if ((inc + fin->fin_plen) > 65535) {
2040 		DT2(eprt6_len, int, inc, int, fin->fin_plen);
2041 		if (softf->ipf_p_ftp_debug & DEBUG_ERROR)
2042 			printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n",
2043 				inc);
2044 		return 0;
2045 	}
2046 
2047 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
2048 #if !defined(_KERNEL)
2049 	M_ADJ(m, inc);
2050 #else
2051 	if (inc < 0)
2052 		M_ADJ(m, inc);
2053 #endif
2054 	/* the mbuf chain will be extended if necessary by m_copyback() */
2055 	COPYBACK(m, off, nlen, newbuf);
2056 	fin->fin_flx |= FI_DOCKSUM;
2057 
2058 	if (inc != 0) {
2059 		fin->fin_plen += inc;
2060 		ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen);
2061 		fin->fin_dlen += inc;
2062 	}
2063 
2064 	f->ftps_cmd = FTPXY_C_EPRT;
2065 	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
2066 }
2067 #endif /* USE_INET6 */
2068