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