xref: /illumos-gate/usr/src/uts/common/inet/ipf/netinet/ip_ftp_pxy.c (revision d4660949aa62dd6a963f4913b7120b383cf473c4)
1 /*
2  * Copyright (C) 1997-2003 by Darren Reed
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * $Id: ip_ftp_pxy.c,v 2.88.2.15 2005/03/19 19:38:10 darrenr Exp $
7  *
8  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
9  * Use is subject to license terms.
10  *
11  * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
12  * code.
13 */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #define	IPF_FTP_PROXY
18 
19 #define	IPF_MINPORTLEN	18
20 #define	IPF_MAXPORTLEN	30
21 #define	IPF_MIN227LEN	39
22 #define	IPF_MAX227LEN	51
23 #define	IPF_MIN229LEN	47
24 #define	IPF_MAX229LEN	51
25 
26 #define	FTPXY_GO	0
27 #define	FTPXY_INIT	1
28 #define	FTPXY_USER_1	2
29 #define	FTPXY_USOK_1	3
30 #define	FTPXY_PASS_1	4
31 #define	FTPXY_PAOK_1	5
32 #define	FTPXY_AUTH_1	6
33 #define	FTPXY_AUOK_1	7
34 #define	FTPXY_ADAT_1	8
35 #define	FTPXY_ADOK_1	9
36 #define	FTPXY_ACCT_1	10
37 #define	FTPXY_ACOK_1	11
38 #define	FTPXY_USER_2	12
39 #define	FTPXY_USOK_2	13
40 #define	FTPXY_PASS_2	14
41 #define	FTPXY_PAOK_2	15
42 
43 /*
44  * Values for FTP commands.  Numerics cover 0-999
45  */
46 #define	FTPXY_C_PASV	1000
47 
48 typedef struct ifs_ftppxy {
49 	frentry_t		ftppxyfr;
50 	int			ftp_proxy_init;
51 	int			ippr_ftp_pasvonly;
52 	int			ippr_ftp_insecure;
53 				/* Do not require logins before transfers */
54 	int			ippr_ftp_pasvrdr;
55 	int			ippr_ftp_forcepasv;
56 				/* PASV must be last command prior to 227 */
57 	/*
58 	 * 1 - security
59 	 * 2 - errors
60 	 * 3 - error debugging
61 	 * 4 - parsing errors
62 	 * 5 - parsing info
63 	 * 6 - parsing debug
64 	 */
65 	int			ippr_ftp_debug;
66 	ipftuneable_t		ftptune;
67 } ifs_ftppxy_t;
68 
69 int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
70     ifs_ftppxy_t *));
71 int ippr_ftp_complete __P((char *, size_t));
72 int ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *, void *));
73 int ippr_ftp_init __P((void **, ipf_stack_t *));
74 void ippr_ftp_fini __P((void **, ipf_stack_t *));
75 int ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *, void *));
76 int ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *, void *));
77 int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
78     ifs_ftppxy_t *));
79 int ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int,
80     ifs_ftppxy_t *));
81 int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int,
82     ifs_ftppxy_t *));
83 int ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int,
84     ifs_ftppxy_t *));
85 int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
86     ifs_ftppxy_t *));
87 int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t, ifs_ftppxy_t *));
88 int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t, ifs_ftppxy_t *));
89 int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t, ifs_ftppxy_t *));
90 u_short ippr_ftp_atoi __P((char **));
91 int ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *,
92 			    u_int, char *, char *, u_int, ifs_ftppxy_t *));
93 
94 /*
95  * Initialize local structures.
96  */
97 int ippr_ftp_init(private, ifs)
98 void **private;
99 ipf_stack_t *ifs;
100 {
101 	ifs_ftppxy_t *ifsftp;
102 
103 	KMALLOC(ifsftp, ifs_ftppxy_t *);
104 	if (ifsftp == NULL)
105 		return -1;
106 
107 	bzero((char *)&ifsftp->ftppxyfr, sizeof(ifsftp->ftppxyfr));
108 	ifsftp->ftppxyfr.fr_ref = 1;
109 	ifsftp->ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
110 	MUTEX_INIT(&ifsftp->ftppxyfr.fr_lock, "FTP Proxy Mutex");
111 	ifsftp->ftp_proxy_init = 1;
112 	ifsftp->ippr_ftp_pasvonly = 0;
113 	ifsftp->ippr_ftp_insecure = 0;
114 	ifsftp->ippr_ftp_pasvrdr = 0;
115 	ifsftp->ippr_ftp_forcepasv = 0;
116 #if defined(_KERNEL)
117 	ifsftp->ippr_ftp_debug = 0;
118 #else
119 	ifsftp->ippr_ftp_debug = 2;
120 #endif
121 	bzero((char *)&ifsftp->ftptune, sizeof(ifsftp->ftptune));
122 	ifsftp->ftptune.ipft_pint = (uint_t *)&ifsftp->ippr_ftp_debug;
123 	ifsftp->ftptune.ipft_name = "ippr_ftp_debug";
124 	ifsftp->ftptune.ipft_max = 10;
125 	ifsftp->ftptune.ipft_sz = sizeof(ifsftp->ippr_ftp_debug);
126 	ifsftp->ftptune.ipft_next = NULL;
127 
128 	(void) fr_addipftune(&ifsftp->ftptune, ifs);
129 
130 	*private = (void *)ifsftp;
131 
132 	return 0;
133 }
134 
135 
136 void ippr_ftp_fini(private, ifs)
137 void **private;
138 ipf_stack_t *ifs;
139 {
140 	ifs_ftppxy_t *ifsftp = *((ifs_ftppxy_t **)private);
141 
142 	(void) fr_delipftune(&ifsftp->ftptune, ifs);
143 
144 	if (ifsftp->ftp_proxy_init == 1) {
145 		MUTEX_DESTROY(&ifsftp->ftppxyfr.fr_lock);
146 		ifsftp->ftp_proxy_init = 0;
147 	}
148 
149 	KFREE(ifsftp);
150 	*private = NULL;
151 }
152 
153 
154 /*ARGSUSED*/
155 int ippr_ftp_new(fin, aps, nat, private)
156 fr_info_t *fin;
157 ap_session_t *aps;
158 nat_t *nat;
159 void *private;
160 {
161 	ftpinfo_t *ftp;
162 	ftpside_t *f;
163 
164 	KMALLOC(ftp, ftpinfo_t *);
165 	if (ftp == NULL)
166 		return -1;
167 
168 	fin = fin;	/* LINT */
169 	nat = nat;	/* LINT */
170 
171 	aps->aps_data = ftp;
172 	aps->aps_psiz = sizeof(ftpinfo_t);
173 
174 	bzero((char *)ftp, sizeof(*ftp));
175 	f = &ftp->ftp_side[0];
176 	f->ftps_rptr = f->ftps_buf;
177 	f->ftps_wptr = f->ftps_buf;
178 	f = &ftp->ftp_side[1];
179 	f->ftps_rptr = f->ftps_buf;
180 	f->ftps_wptr = f->ftps_buf;
181 	ftp->ftp_passok = FTPXY_INIT;
182 	ftp->ftp_incok = 0;
183 	return 0;
184 }
185 
186 
187 int ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp)
188 fr_info_t *fin;
189 ip_t *ip;
190 nat_t *nat;
191 ftpside_t *f;
192 int dlen;
193 ifs_ftppxy_t *ifsftp;
194 {
195 	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
196 	char newbuf[IPF_FTPBUFSZ], *s;
197 	struct in_addr swip, swip2;
198 	u_int a1, a2, a3, a4;
199 	int inc, off, flags;
200 	u_short a5, a6, sp;
201 	size_t nlen, olen;
202 	fr_info_t fi;
203 	nat_t *nat2;
204 	mb_t *m;
205 	ipf_stack_t *ifs = fin->fin_ifs;
206 
207 	m = fin->fin_m;
208 	tcp = (tcphdr_t *)fin->fin_dp;
209 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
210 
211 	/*
212 	 * Check for client sending out PORT message.
213 	 */
214 	if (dlen < IPF_MINPORTLEN) {
215 		if (ifsftp->ippr_ftp_debug > 1)
216 			printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
217 			       dlen);
218 		return 0;
219 	}
220 	/*
221 	 * Skip the PORT command + space
222 	 */
223 	s = f->ftps_rptr + 5;
224 	/*
225 	 * Pick out the address components, two at a time.
226 	 */
227 	a1 = ippr_ftp_atoi(&s);
228 	if (s == NULL) {
229 		if (ifsftp->ippr_ftp_debug > 1)
230 			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1);
231 		return 0;
232 	}
233 	a2 = ippr_ftp_atoi(&s);
234 	if (s == NULL) {
235 		if (ifsftp->ippr_ftp_debug > 1)
236 			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2);
237 		return 0;
238 	}
239 
240 	/*
241 	 * Check that IP address in the PORT/PASV reply is the same as the
242 	 * sender of the command - prevents using PORT for port scanning.
243 	 */
244 	a1 <<= 16;
245 	a1 |= a2;
246 	if (((nat->nat_dir == NAT_OUTBOUND) &&
247 	     (a1 != ntohl(nat->nat_inip.s_addr))) ||
248 	    ((nat->nat_dir == NAT_INBOUND) &&
249 	     (a1 != ntohl(nat->nat_oip.s_addr)))) {
250 		if (ifsftp->ippr_ftp_debug > 0)
251 			printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1");
252 		return APR_ERR(1);
253 	}
254 
255 	a5 = ippr_ftp_atoi(&s);
256 	if (s == NULL) {
257 		if (ifsftp->ippr_ftp_debug > 1)
258 			printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3);
259 		return 0;
260 	}
261 	if (*s == ')')
262 		s++;
263 
264 	/*
265 	 * check for CR-LF at the end.
266 	 */
267 	if (*s == '\n')
268 		s--;
269 	if ((*s == '\r') && (*(s + 1) == '\n')) {
270 		s += 2;
271 		a6 = a5 & 0xff;
272 	} else {
273 		if (ifsftp->ippr_ftp_debug > 1)
274 			printf("ippr_ftp_port:missing %s\n", "cr-lf");
275 		return 0;
276 	}
277 
278 	a5 >>= 8;
279 	a5 &= 0xff;
280 	sp = a5 << 8 | a6;
281 	/*
282 	 * Don't allow the PORT command to specify a port < 1024 due to
283 	 * security crap.
284 	 */
285 	if (sp < 1024) {
286 		if (ifsftp->ippr_ftp_debug > 0)
287 			printf("ippr_ftp_port:sp(%d) < 1024\n", sp);
288 		return 0;
289 	}
290 	/*
291 	 * Calculate new address parts for PORT command
292 	 */
293 	if (nat->nat_dir == NAT_INBOUND)
294 		a1 = ntohl(nat->nat_oip.s_addr);
295 	else
296 		a1 = ntohl(ip->ip_src.s_addr);
297 	a2 = (a1 >> 16) & 0xff;
298 	a3 = (a1 >> 8) & 0xff;
299 	a4 = a1 & 0xff;
300 	a1 >>= 24;
301 	olen = s - f->ftps_rptr;
302 	/* DO NOT change this to snprintf! */
303 #if defined(SNPRINTF) && defined(_KERNEL)
304 	(void) SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
305 		 "PORT", a1, a2, a3, a4, a5, a6);
306 #else
307 	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
308 		       "PORT", a1, a2, a3, a4, a5, a6);
309 #endif
310 
311 	nlen = strlen(newbuf);
312 	inc = nlen - olen;
313 	if ((inc + ip->ip_len) > 65535) {
314 		if (ifsftp->ippr_ftp_debug > 0)
315 			printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n",
316 			       inc);
317 		return 0;
318 	}
319 
320 #if !defined(_KERNEL)
321 	bcopy(newbuf, MTOD(m, char *) + off, nlen);
322 #else
323 # if defined(MENTAT)
324 	if (inc < 0)
325 		(void)adjmsg(m, inc);
326 # else /* defined(MENTAT) */
327 	/*
328 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
329 	 * mean remove -len bytes from the end of the packet.
330 	 * The mbuf chain will be extended if necessary by m_copyback().
331 	 */
332 	if (inc < 0)
333 		m_adj(m, inc);
334 # endif /* defined(MENTAT) */
335 #endif /* !defined(_KERNEL) */
336 	COPYBACK(m, off, nlen, newbuf);
337 
338 	if (inc != 0) {
339 		ip->ip_len += inc;
340 		fin->fin_dlen += inc;
341 		fin->fin_plen += inc;
342 	}
343 
344 	/*
345 	 * The server may not make the connection back from port 20, but
346 	 * it is the most likely so use it here to check for a conflicting
347 	 * mapping.
348 	 */
349 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
350 	fi.fin_state = NULL;
351 	fi.fin_nat = NULL;
352 	fi.fin_flx |= FI_IGNORE;
353 	fi.fin_data[0] = sp;
354 	fi.fin_data[1] = fin->fin_data[1] - 1;
355 	/*
356 	 * Add skeleton NAT entry for connection which will come back the
357 	 * other way.
358 	 */
359 	if (nat->nat_dir == NAT_OUTBOUND)
360 		nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
361 				     nat->nat_inip, nat->nat_oip);
362 	else
363 		nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
364 				    nat->nat_inip, nat->nat_oip);
365 	if (nat2 == NULL) {
366 		int slen;
367 
368 		slen = ip->ip_len;
369 		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
370 		bzero((char *)tcp2, sizeof(*tcp2));
371 		tcp2->th_win = htons(8192);
372 		tcp2->th_sport = htons(sp);
373 		TCP_OFF_A(tcp2, 5);
374 		tcp2->th_flags = TH_SYN;
375 		tcp2->th_dport = 0; /* XXX - don't specify remote port */
376 		fi.fin_data[1] = 0;
377 		fi.fin_dlen = sizeof(*tcp2);
378 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
379 		fi.fin_dp = (char *)tcp2;
380 		fi.fin_fr = &ifsftp->ftppxyfr;
381 		fi.fin_out = nat->nat_dir;
382 		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
383 		swip = ip->ip_src;
384 		swip2 = ip->ip_dst;
385 		if (nat->nat_dir == NAT_OUTBOUND) {
386 			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
387 			ip->ip_src = nat->nat_inip;
388 		} else if (nat->nat_dir == NAT_INBOUND) {
389 			fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
390 			ip->ip_src = nat->nat_oip;
391 		}
392 
393 		flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
394 		if (nat->nat_dir == NAT_INBOUND)
395 			flags |= NAT_NOTRULEPORT;
396 		nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir);
397 
398 		if (nat2 != NULL) {
399 			(void) nat_proto(&fi, nat2, IPN_TCP);
400 			nat_update(&fi, nat2, nat->nat_ptr);
401 			fi.fin_ifp = NULL;
402 			if (nat->nat_dir == NAT_INBOUND) {
403 				fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
404 				ip->ip_dst = nat->nat_inip;
405 			}
406 			(void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT);
407 			if (fi.fin_state != NULL)
408 				fr_statederef((ipstate_t **)&fi.fin_state, ifs);
409 		}
410 		ip->ip_len = slen;
411 		ip->ip_src = swip;
412 		ip->ip_dst = swip2;
413 	} else {
414 		ipstate_t *is;
415 
416 		nat_update(&fi, nat2, nat->nat_ptr);
417 		READ_ENTER(&ifs->ifs_ipf_state);
418 		is = nat2->nat_state;
419 		if (is != NULL) {
420 			MUTEX_ENTER(&is->is_lock);
421 			(void)fr_tcp_age(&is->is_sti, &fi, ifs->ifs_ips_tqtqb,
422 				         is->is_flags);
423 			MUTEX_EXIT(&is->is_lock);
424 		}
425 		RWLOCK_EXIT(&ifs->ifs_ipf_state);
426 	}
427 	return APR_INC(inc);
428 }
429 
430 
431 int ippr_ftp_client(fin, ip, nat, ftp, dlen, ifsftp)
432 fr_info_t *fin;
433 nat_t *nat;
434 ftpinfo_t *ftp;
435 ip_t *ip;
436 int dlen;
437 ifs_ftppxy_t *ifsftp;
438 {
439 	char *rptr, *wptr, cmd[6], c;
440 	ftpside_t *f;
441 	int inc, i;
442 
443 	inc = 0;
444 	f = &ftp->ftp_side[0];
445 	rptr = f->ftps_rptr;
446 	wptr = f->ftps_wptr;
447 
448 	for (i = 0; (i < 5) && (i < dlen); i++) {
449 		c = rptr[i];
450 		if (ISALPHA(c)) {
451 			cmd[i] = TOUPPER(c);
452 		} else {
453 			cmd[i] = c;
454 		}
455 	}
456 	cmd[i] = '\0';
457 
458 	ftp->ftp_incok = 0;
459 	if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
460 		if (ftp->ftp_passok == FTPXY_ADOK_1 ||
461 		    ftp->ftp_passok == FTPXY_AUOK_1) {
462 			ftp->ftp_passok = FTPXY_USER_2;
463 			ftp->ftp_incok = 1;
464 		} else {
465 			ftp->ftp_passok = FTPXY_USER_1;
466 			ftp->ftp_incok = 1;
467 		}
468 	} else if (!strncmp(cmd, "AUTH ", 5)) {
469 		ftp->ftp_passok = FTPXY_AUTH_1;
470 		ftp->ftp_incok = 1;
471 	} else if (!strncmp(cmd, "PASS ", 5)) {
472 		if (ftp->ftp_passok == FTPXY_USOK_1) {
473 			ftp->ftp_passok = FTPXY_PASS_1;
474 			ftp->ftp_incok = 1;
475 		} else if (ftp->ftp_passok == FTPXY_USOK_2) {
476 			ftp->ftp_passok = FTPXY_PASS_2;
477 			ftp->ftp_incok = 1;
478 		}
479 	} else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
480 		   !strncmp(cmd, "ADAT ", 5)) {
481 		ftp->ftp_passok = FTPXY_ADAT_1;
482 		ftp->ftp_incok = 1;
483 	} else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
484 		    ftp->ftp_passok == FTPXY_PAOK_2) &&
485 		 !strncmp(cmd, "ACCT ", 5)) {
486 		ftp->ftp_passok = FTPXY_ACCT_1;
487 		ftp->ftp_incok = 1;
488 	} else if ((ftp->ftp_passok == FTPXY_GO) &&
489 		 !ifsftp->ippr_ftp_pasvonly &&
490 		 !strncmp(cmd, "PORT ", 5)) {
491 		inc = ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp);
492 	} else if (ifsftp->ippr_ftp_insecure && !ifsftp->ippr_ftp_pasvonly &&
493 		   !strncmp(cmd, "PORT ", 5)) {
494 		inc = ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp);
495 	}
496 
497 	while ((*rptr++ != '\n') && (rptr < wptr))
498 		;
499 	f->ftps_rptr = rptr;
500 	return inc;
501 }
502 
503 
504 int ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp)
505 fr_info_t *fin;
506 ip_t *ip;
507 nat_t *nat;
508 ftpinfo_t *ftp;
509 int dlen;
510 ifs_ftppxy_t *ifsftp;
511 {
512 	u_int a1, a2, a3, a4, data_ip;
513 	char newbuf[IPF_FTPBUFSZ];
514 	char *s, *brackets[2];
515 	u_short a5, a6;
516 	ftpside_t *f;
517 
518 	if (ifsftp->ippr_ftp_forcepasv != 0 &&
519 	    ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) {
520 		if (ifsftp->ippr_ftp_debug > 0)
521 			printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
522 			       ftp->ftp_side[0].ftps_cmds);
523 		return 0;
524 	}
525 
526 	f = &ftp->ftp_side[1];
527 
528 #define	PASV_REPLEN	24
529 	/*
530 	 * Check for PASV reply message.
531 	 */
532 	if (dlen < IPF_MIN227LEN) {
533 		if (ifsftp->ippr_ftp_debug > 1)
534 			printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
535 			       dlen);
536 		return 0;
537 	} else if (strncmp(f->ftps_rptr,
538 			   "227 Entering Passive Mod", PASV_REPLEN)) {
539 		if (ifsftp->ippr_ftp_debug > 0)
540 			printf("ippr_ftp_pasv:%d reply wrong\n", 227);
541 		return 0;
542 	}
543 
544 	brackets[0] = "";
545 	brackets[1] = "";
546 	/*
547 	 * Skip the PASV reply + space
548 	 */
549 	s = f->ftps_rptr + PASV_REPLEN;
550 	while (*s && !ISDIGIT(*s)) {
551 		if (*s == '(') {
552 			brackets[0] = "(";
553 			brackets[1] = ")";
554 		}
555 		s++;
556 	}
557 
558 	/*
559 	 * Pick out the address components, two at a time.
560 	 */
561 	a1 = ippr_ftp_atoi(&s);
562 	if (s == NULL) {
563 		if (ifsftp->ippr_ftp_debug > 1)
564 			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1);
565 		return 0;
566 	}
567 	a2 = ippr_ftp_atoi(&s);
568 	if (s == NULL) {
569 		if (ifsftp->ippr_ftp_debug > 1)
570 			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2);
571 		return 0;
572 	}
573 
574 	/*
575 	 * check that IP address in the PASV reply is the same as the
576 	 * sender of the command - prevents using PASV for port scanning.
577 	 */
578 	a1 <<= 16;
579 	a1 |= a2;
580 
581 	if (((nat->nat_dir == NAT_INBOUND) &&
582 	     (a1 != ntohl(nat->nat_inip.s_addr))) ||
583 	    ((nat->nat_dir == NAT_OUTBOUND) &&
584 	     (a1 != ntohl(nat->nat_oip.s_addr)))) {
585 		if (ifsftp->ippr_ftp_debug > 0)
586 			printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1");
587 		return 0;
588 	}
589 
590 	a5 = ippr_ftp_atoi(&s);
591 	if (s == NULL) {
592 		if (ifsftp->ippr_ftp_debug > 1)
593 			printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3);
594 		return 0;
595 	}
596 
597 	if (*s == ')')
598 		s++;
599 	if (*s == '.')
600 		s++;
601 	if (*s == '\n')
602 		s--;
603 	/*
604 	 * check for CR-LF at the end.
605 	 */
606 	if ((*s == '\r') && (*(s + 1) == '\n')) {
607 		s += 2;
608 	} else {
609 		if (ifsftp->ippr_ftp_debug > 1)
610 			printf("ippr_ftp_pasv:missing %s", "cr-lf\n");
611 		return 0;
612 	}
613 
614 	a6 = a5 & 0xff;
615 	a5 >>= 8;
616 	/*
617 	 * Calculate new address parts for 227 reply
618 	 */
619 	if (nat->nat_dir == NAT_INBOUND) {
620 		data_ip = nat->nat_outip.s_addr;
621 		a1 = ntohl(data_ip);
622 	} else
623 		data_ip = htonl(a1);
624 
625 	a2 = (a1 >> 16) & 0xff;
626 	a3 = (a1 >> 8) & 0xff;
627 	a4 = a1 & 0xff;
628 	a1 >>= 24;
629 
630 #if defined(SNPRINTF) && defined(_KERNEL)
631 	(void) SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
632 		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
633 		a5, a6, brackets[1]);
634 #else
635 	(void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
636 		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
637 		a5, a6, brackets[1]);
638 #endif
639 	return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6),
640 				  newbuf, s, data_ip, ifsftp);
641 }
642 
643 int ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip, ifsftp)
644 fr_info_t *fin;
645 ip_t *ip;
646 nat_t *nat;
647 ftpside_t *f;
648 u_int port;
649 char *newmsg;
650 char *s;
651 u_int data_ip;
652 ifs_ftppxy_t *ifsftp;
653 {
654 	int inc, off, nflags, sflags;
655 	tcphdr_t *tcp, tcph, *tcp2;
656 	struct in_addr swip, swip2;
657 	struct in_addr data_addr;
658 	size_t nlen, olen;
659 	fr_info_t fi;
660 	nat_t *nat2;
661 	mb_t *m;
662 	ipf_stack_t *ifs = fin->fin_ifs;
663 
664 	m = fin->fin_m;
665 	tcp = (tcphdr_t *)fin->fin_dp;
666 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
667 
668 	data_addr.s_addr = data_ip;
669 	tcp2 = &tcph;
670 	inc = 0;
671 
672 
673 	olen = s - f->ftps_rptr;
674 	nlen = strlen(newmsg);
675 	inc = nlen - olen;
676 	if ((inc + ip->ip_len) > 65535) {
677 		if (ifsftp->ippr_ftp_debug > 0)
678 			printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
679 			       inc);
680 		return 0;
681 	}
682 
683 #if !defined(_KERNEL)
684 	bcopy(newmsg, MTOD(m, char *) + off, nlen);
685 #else
686 # if defined(MENTAT)
687 	if (inc < 0)
688 		(void)adjmsg(m, inc);
689 # else /* defined(MENTAT) */
690 	/*
691 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
692 	 * mean remove -len bytes from the end of the packet.
693 	 * The mbuf chain will be extended if necessary by m_copyback().
694 	 */
695 	if (inc < 0)
696 		m_adj(m, inc);
697 # endif /* defined(MENTAT) */
698 #endif /* !defined(_KERNEL) */
699 	COPYBACK(m, off, nlen, newmsg);
700 
701 	if (inc != 0) {
702 		ip->ip_len += inc;
703 		fin->fin_dlen += inc;
704 		fin->fin_plen += inc;
705 	}
706 
707 	/*
708 	 * Add skeleton NAT entry for connection which will come back the
709 	 * other way.
710 	 */
711 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
712 	fi.fin_state = NULL;
713 	fi.fin_nat = NULL;
714 	fi.fin_flx |= FI_IGNORE;
715 	fi.fin_data[0] = 0;
716 	fi.fin_data[1] = port;
717 	nflags = IPN_TCP|SI_W_SPORT;
718 	if (ifsftp->ippr_ftp_pasvrdr && f->ftps_ifp)
719 		nflags |= SI_W_DPORT;
720 	if (nat->nat_dir == NAT_OUTBOUND)
721 		nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH,
722 				     nat->nat_p, nat->nat_inip, nat->nat_oip);
723 	else
724 		nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH,
725 				    nat->nat_p, nat->nat_inip, nat->nat_oip);
726 	if (nat2 == NULL) {
727 		int slen;
728 
729 		slen = ip->ip_len;
730 		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
731 		bzero((char *)tcp2, sizeof(*tcp2));
732 		tcp2->th_win = htons(8192);
733 		tcp2->th_sport = 0;		/* XXX - fake it for nat_new */
734 		TCP_OFF_A(tcp2, 5);
735 		tcp2->th_flags = TH_SYN;
736 		fi.fin_data[1] = port;
737 		fi.fin_dlen = sizeof(*tcp2);
738 		tcp2->th_dport = htons(port);
739 		fi.fin_data[0] = 0;
740 		fi.fin_dp = (char *)tcp2;
741 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
742 		fi.fin_fr = &ifsftp->ftppxyfr;
743 		fi.fin_out = nat->nat_dir;
744 		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
745 		swip = ip->ip_src;
746 		swip2 = ip->ip_dst;
747 		if (nat->nat_dir == NAT_OUTBOUND) {
748 			fi.fin_fi.fi_daddr = data_addr.s_addr;
749 			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
750 			ip->ip_dst = data_addr;
751 			ip->ip_src = nat->nat_inip;
752 		} else if (nat->nat_dir == NAT_INBOUND) {
753 			fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
754 			fi.fin_fi.fi_daddr = nat->nat_outip.s_addr;
755 			ip->ip_src = nat->nat_oip;
756 			ip->ip_dst = nat->nat_outip;
757 		}
758 
759 		sflags = nflags;
760 		nflags |= NAT_SLAVE;
761 		if (nat->nat_dir == NAT_INBOUND)
762 			nflags |= NAT_NOTRULEPORT;
763 		nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir);
764 		if (nat2 != NULL) {
765 			(void) nat_proto(&fi, nat2, IPN_TCP);
766 			nat_update(&fi, nat2, nat->nat_ptr);
767 			fi.fin_ifp = NULL;
768 			if (nat->nat_dir == NAT_INBOUND) {
769 				fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
770 				ip->ip_dst = nat->nat_inip;
771 			}
772 			(void) fr_addstate(&fi, &nat2->nat_state, sflags);
773 			if (fi.fin_state != NULL)
774 				fr_statederef((ipstate_t **)&fi.fin_state, ifs);
775 		}
776 
777 		ip->ip_len = slen;
778 		ip->ip_src = swip;
779 		ip->ip_dst = swip2;
780 	} else {
781 		ipstate_t *is;
782 
783 		nat_update(&fi, nat2, nat->nat_ptr);
784 		READ_ENTER(&ifs->ifs_ipf_state);
785 		is = nat2->nat_state;
786 		if (is != NULL) {
787 			MUTEX_ENTER(&is->is_lock);
788 			(void) fr_tcp_age(&is->is_sti, &fi, ifs->ifs_ips_tqtqb,
789 					  is->is_flags);
790 			MUTEX_EXIT(&is->is_lock);
791 		}
792 		RWLOCK_EXIT(&ifs->ifs_ipf_state);
793 	}
794 	return inc;
795 }
796 
797 
798 int ippr_ftp_server(fin, ip, nat, ftp, dlen, ifsftp)
799 fr_info_t *fin;
800 ip_t *ip;
801 nat_t *nat;
802 ftpinfo_t *ftp;
803 int dlen;
804 ifs_ftppxy_t *ifsftp;
805 {
806 	char *rptr, *wptr;
807 	ftpside_t *f;
808 	int inc;
809 
810 	inc = 0;
811 	f = &ftp->ftp_side[1];
812 	rptr = f->ftps_rptr;
813 	wptr = f->ftps_wptr;
814 
815 	if (*rptr == ' ')
816 		goto server_cmd_ok;
817 	if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
818 		return 0;
819 	if (ftp->ftp_passok == FTPXY_GO) {
820 		if (!strncmp(rptr, "227 ", 4))
821 			inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp);
822 		else if (!strncmp(rptr, "229 ", 4))
823 			inc = ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp);
824 	} else if (ifsftp->ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
825 		inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp);
826 	} else if (ifsftp->ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
827 		inc = ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp);
828 	} else if (*rptr == '5' || *rptr == '4')
829 		ftp->ftp_passok = FTPXY_INIT;
830 	else if (ftp->ftp_incok) {
831 		if (*rptr == '3') {
832 			if (ftp->ftp_passok == FTPXY_ACCT_1)
833 				ftp->ftp_passok = FTPXY_GO;
834 			else
835 				ftp->ftp_passok++;
836 		} else if (*rptr == '2') {
837 			switch (ftp->ftp_passok)
838 			{
839 			case FTPXY_USER_1 :
840 			case FTPXY_USER_2 :
841 			case FTPXY_PASS_1 :
842 			case FTPXY_PASS_2 :
843 			case FTPXY_ACCT_1 :
844 				ftp->ftp_passok = FTPXY_GO;
845 				break;
846 			default :
847 				ftp->ftp_passok += 3;
848 				break;
849 			}
850 		}
851 	}
852 server_cmd_ok:
853 	ftp->ftp_incok = 0;
854 
855 	while ((*rptr++ != '\n') && (rptr < wptr))
856 		;
857 	f->ftps_rptr = rptr;
858 	return inc;
859 }
860 
861 
862 /*
863  * Look to see if the buffer starts with something which we recognise as
864  * being the correct syntax for the FTP protocol.
865  */
866 int ippr_ftp_client_valid(ftps, buf, len, ifsftp)
867 ftpside_t *ftps;
868 char *buf;
869 size_t len;
870 ifs_ftppxy_t *ifsftp;
871 {
872 	register char *s, c, pc;
873 	register size_t i = len;
874 	char cmd[5];
875 
876 	s = buf;
877 
878 	if (ftps->ftps_junk == 1)
879 		return 1;
880 
881 	if (i < 5) {
882 		if (ifsftp->ippr_ftp_debug > 3)
883 			printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i);
884 		return 2;
885 	}
886 
887 	i--;
888 	c = *s++;
889 
890 	if (ISALPHA(c)) {
891 		cmd[0] = TOUPPER(c);
892 		c = *s++;
893 		i--;
894 		if (ISALPHA(c)) {
895 			cmd[1] = TOUPPER(c);
896 			c = *s++;
897 			i--;
898 			if (ISALPHA(c)) {
899 				cmd[2] = TOUPPER(c);
900 				c = *s++;
901 				i--;
902 				if (ISALPHA(c)) {
903 					cmd[3] = TOUPPER(c);
904 					c = *s++;
905 					i--;
906 					if ((c != ' ') && (c != '\r'))
907 						goto bad_client_command;
908 				} else if ((c != ' ') && (c != '\r'))
909 					goto bad_client_command;
910 			} else
911 				goto bad_client_command;
912 		} else
913 			goto bad_client_command;
914 	} else {
915 bad_client_command:
916 		if (ifsftp->ippr_ftp_debug > 3)
917 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*s]\n",
918 			       "ippr_ftp_client_valid",
919 			       ftps->ftps_junk, (int)len, (int)i, c,
920 			       (int)len, buf);
921 		return 1;
922 	}
923 
924 	for (; i; i--) {
925 		pc = c;
926 		c = *s++;
927 		if ((pc == '\r') && (c == '\n')) {
928 			cmd[4] = '\0';
929 			if (!strcmp(cmd, "PASV"))
930 				ftps->ftps_cmds = FTPXY_C_PASV;
931 			else
932 				ftps->ftps_cmds = 0;
933 			return 0;
934 		}
935 	}
936 #if !defined(_KERNEL)
937 	printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n",
938 	       (int)len, (int)len, buf);
939 #endif
940 	return 2;
941 }
942 
943 
944 int ippr_ftp_server_valid(ftps, buf, len, ifsftp)
945 ftpside_t *ftps;
946 char *buf;
947 size_t len;
948 ifs_ftppxy_t *ifsftp;
949 {
950 	register char *s, c, pc;
951 	register size_t i = len;
952 	int cmd;
953 
954 	s = buf;
955 	cmd = 0;
956 
957 	if (ftps->ftps_junk == 1)
958 		return 1;
959 
960 	if (i < 5) {
961 		if (ifsftp->ippr_ftp_debug > 3)
962 			printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i);
963 		return 2;
964 	}
965 
966 	c = *s++;
967 	i--;
968 	if (c == ' ')
969 		goto search_eol;
970 
971 	if (ISDIGIT(c)) {
972 		cmd = (c - '0') * 100;
973 		c = *s++;
974 		i--;
975 		if (ISDIGIT(c)) {
976 			cmd += (c - '0') * 10;
977 			c = *s++;
978 			i--;
979 			if (ISDIGIT(c)) {
980 				cmd += (c - '0');
981 				c = *s++;
982 				i--;
983 				if ((c != '-') && (c != ' '))
984 					goto bad_server_command;
985 			} else
986 				goto bad_server_command;
987 		} else
988 			goto bad_server_command;
989 	} else {
990 bad_server_command:
991 		if (ifsftp->ippr_ftp_debug > 3)
992 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*s]\n",
993 			       "ippr_ftp_server_valid",
994 			       ftps->ftps_junk, (int)len, (int)i,
995 			       c, (int)len, buf);
996 		return 1;
997 	}
998 search_eol:
999 	for (; i; i--) {
1000 		pc = c;
1001 		c = *s++;
1002 		if ((pc == '\r') && (c == '\n')) {
1003 			ftps->ftps_cmds = cmd;
1004 			return 0;
1005 		}
1006 	}
1007 	if (ifsftp->ippr_ftp_debug > 3)
1008 		printf("ippr_ftp_server_valid:junk after cmd[%*s]\n",
1009 		       (int)len, buf);
1010 	return 2;
1011 }
1012 
1013 
1014 int ippr_ftp_valid(ftp, side, buf, len, ifsftp)
1015 ftpinfo_t *ftp;
1016 int side;
1017 char *buf;
1018 size_t len;
1019 ifs_ftppxy_t *ifsftp;
1020 {
1021 	ftpside_t *ftps;
1022 	int ret;
1023 
1024 	ftps = &ftp->ftp_side[side];
1025 
1026 	if (side == 0)
1027 		ret = ippr_ftp_client_valid(ftps, buf, len, ifsftp);
1028 	else
1029 		ret = ippr_ftp_server_valid(ftps, buf, len, ifsftp);
1030 	return ret;
1031 }
1032 
1033 
1034 /*
1035  * For map rules, the following applies:
1036  * rv == 0 for outbound processing,
1037  * rv == 1 for inbound processing.
1038  * For rdr rules, the following applies:
1039  * rv == 0 for inbound processing,
1040  * rv == 1 for outbound processing.
1041  */
1042 int ippr_ftp_process(fin, nat, ftp, rv, ifsftp)
1043 fr_info_t *fin;
1044 nat_t *nat;
1045 ftpinfo_t *ftp;
1046 int rv;
1047 ifs_ftppxy_t *ifsftp;
1048 {
1049 	int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff;
1050 	char *rptr, *wptr, *s;
1051 	u_32_t thseq, thack;
1052 	ap_session_t *aps;
1053 	ftpside_t *f, *t;
1054 	tcphdr_t *tcp;
1055 	ip_t *ip;
1056 	mb_t *m;
1057 
1058 	m = fin->fin_m;
1059 	ip = fin->fin_ip;
1060 	tcp = (tcphdr_t *)fin->fin_dp;
1061 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1062 
1063 	f = &ftp->ftp_side[rv];
1064 	t = &ftp->ftp_side[1 - rv];
1065 	thseq = ntohl(tcp->th_seq);
1066 	thack = ntohl(tcp->th_ack);
1067 
1068 #ifdef __sgi
1069 	mlen = fin->fin_plen - off;
1070 #else
1071 	mlen = MSGDSIZE(m) - off;
1072 #endif
1073 	if (ifsftp->ippr_ftp_debug > 4)
1074 		printf("ippr_ftp_process: mlen %d\n", mlen);
1075 
1076 	if (mlen <= 0) {
1077 		if ((tcp->th_flags & TH_OPENING) == TH_OPENING) {
1078 			f->ftps_seq[0] = thseq + 1;
1079 			t->ftps_seq[0] = thack;
1080 		}
1081 		return 0;
1082 	}
1083 	aps = nat->nat_aps;
1084 
1085 	sel = aps->aps_sel[1 - rv];
1086 	sel2 = aps->aps_sel[rv];
1087 	if (rv == 0) {
1088 		seqoff = aps->aps_seqoff[sel];
1089 		if (aps->aps_seqmin[sel] > seqoff + thseq)
1090 			seqoff = aps->aps_seqoff[!sel];
1091 		ackoff = aps->aps_ackoff[sel2];
1092 		if (aps->aps_ackmin[sel2] > ackoff + thack)
1093 			ackoff = aps->aps_ackoff[!sel2];
1094 	} else {
1095 		seqoff = aps->aps_ackoff[sel];
1096 		if (ifsftp->ippr_ftp_debug > 2)
1097 			printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1098 			       aps->aps_ackmin[sel]);
1099 		if (aps->aps_ackmin[sel] > seqoff + thseq)
1100 			seqoff = aps->aps_ackoff[!sel];
1101 
1102 		ackoff = aps->aps_seqoff[sel2];
1103 		if (ifsftp->ippr_ftp_debug > 2)
1104 			printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1105 			       aps->aps_seqmin[sel2]);
1106 		if (ackoff > 0) {
1107 			if (aps->aps_seqmin[sel2] > ackoff + thack)
1108 				ackoff = aps->aps_seqoff[!sel2];
1109 		} else {
1110 			if (aps->aps_seqmin[sel2] > thack)
1111 				ackoff = aps->aps_seqoff[!sel2];
1112 		}
1113 	}
1114 	if (ifsftp->ippr_ftp_debug > 2) {
1115 		printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1116 		       rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1117 		       thack, ackoff, mlen, fin->fin_plen, off);
1118 		printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1119 		       aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1120 		       aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1121 		printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1122 		       aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1123 		       aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1124 	}
1125 
1126 	/*
1127 	 * XXX - Ideally, this packet should get dropped because we now know
1128 	 * that it is out of order (and there is no real danger in doing so
1129 	 * apart from causing packets to go through here ordered).
1130 	 */
1131 	if (ifsftp->ippr_ftp_debug > 2) {
1132 		printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1133 		       rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1134 	}
1135 
1136 	ok = 0;
1137 	if (t->ftps_seq[0] == 0) {
1138 		t->ftps_seq[0] = thack;
1139 		ok = 1;
1140 	} else {
1141 		if (ackoff == 0) {
1142 			if (t->ftps_seq[0] == thack)
1143 				ok = 1;
1144 			else if (t->ftps_seq[1] == thack) {
1145 				t->ftps_seq[0] = thack;
1146 				ok = 1;
1147 			}
1148 		} else {
1149 			if (t->ftps_seq[0] + ackoff == thack)
1150 				ok = 1;
1151 			else if (t->ftps_seq[0] == thack + ackoff)
1152 				ok = 1;
1153 			else if (t->ftps_seq[1] + ackoff == thack) {
1154 				t->ftps_seq[0] = thack - ackoff;
1155 				ok = 1;
1156 			} else if (t->ftps_seq[1] == thack + ackoff) {
1157 				t->ftps_seq[0] = thack - ackoff;
1158 				ok = 1;
1159 			}
1160 		}
1161 	}
1162 
1163 	if (ifsftp->ippr_ftp_debug > 2) {
1164 		if (!ok)
1165 			printf("%s ok\n", "not");
1166 	}
1167 
1168 	if (!mlen) {
1169 		if (t->ftps_seq[0] + ackoff != thack) {
1170 			if (ifsftp->ippr_ftp_debug > 1) {
1171 				printf("%s:seq[0](%x) + (%x) != (%x)\n",
1172 				       "ippr_ftp_process", t->ftps_seq[0],
1173 				       ackoff, thack);
1174 			}
1175 			return APR_ERR(1);
1176 		}
1177 
1178 		if (ifsftp->ippr_ftp_debug > 2) {
1179 			printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n",
1180 				f->ftps_seq[0], f->ftps_seq[1]);
1181 		}
1182 
1183 		if (tcp->th_flags & TH_FIN) {
1184 			if (thseq == f->ftps_seq[1]) {
1185 				f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1186 				f->ftps_seq[1] = thseq + 1 - seqoff;
1187 			} else {
1188 				if (ifsftp->ippr_ftp_debug > 1) {
1189 					printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1190 					       thseq, seqoff, f->ftps_seq[0]);
1191 				}
1192 				return APR_ERR(1);
1193 			}
1194 		}
1195 		f->ftps_len = 0;
1196 		return 0;
1197 	}
1198 
1199 	ok = 0;
1200 	if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1201 		ok = 1;
1202 	/*
1203 	 * Retransmitted data packet.
1204 	 */
1205 	} else if ((thseq + mlen == f->ftps_seq[0]) ||
1206 		   (thseq + mlen == f->ftps_seq[1])) {
1207 		ok = 1;
1208 	}
1209 
1210 	if (ok == 0) {
1211 		inc = thseq - f->ftps_seq[0];
1212 		if (ifsftp->ippr_ftp_debug > 1) {
1213 			printf("inc %d sel %d rv %d\n", inc, sel, rv);
1214 			printf("th_seq %x ftps_seq %x/%x\n",
1215 			       thseq, f->ftps_seq[0], f->ftps_seq[1]);
1216 			printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1217 			       aps->aps_ackoff[sel]);
1218 			printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1219 			       aps->aps_seqoff[sel]);
1220 		}
1221 
1222 		return APR_ERR(1);
1223 	}
1224 
1225 	inc = 0;
1226 	rptr = f->ftps_rptr;
1227 	wptr = f->ftps_wptr;
1228 	f->ftps_seq[0] = thseq;
1229 	f->ftps_seq[1] = f->ftps_seq[0] + mlen;
1230 	f->ftps_len = mlen;
1231 
1232 	while (mlen > 0) {
1233 		len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1234 		COPYDATA(m, off, len, wptr);
1235 		mlen -= len;
1236 		off += len;
1237 		wptr += len;
1238 
1239 		if (ifsftp->ippr_ftp_debug > 3)
1240 			printf("%s:len %d/%d off %d wptr %lx junk %d [%*s]\n",
1241 			       "ippr_ftp_process",
1242 			       len, mlen, off, (u_long)wptr, f->ftps_junk,
1243 			       len, rptr);
1244 
1245 		f->ftps_wptr = wptr;
1246 		if (f->ftps_junk != 0) {
1247 			i = f->ftps_junk;
1248 			f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1249 						      wptr - rptr, ifsftp);
1250 
1251 			if (ifsftp->ippr_ftp_debug > 5)
1252 				printf("%s:junk %d -> %d\n",
1253 				       "ippr_ftp_process", i, f->ftps_junk);
1254 
1255 			if (f->ftps_junk != 0) {
1256 				if (wptr - rptr == sizeof(f->ftps_buf)) {
1257 					if (ifsftp->ippr_ftp_debug > 4)
1258 						printf("%s:full buffer\n",
1259 						       "ippr_ftp_process");
1260 					f->ftps_rptr = f->ftps_buf;
1261 					f->ftps_wptr = f->ftps_buf;
1262 					rptr = f->ftps_rptr;
1263 					wptr = f->ftps_wptr;
1264 					/*
1265 					 * Because we throw away data here that
1266 					 * we would otherwise parse, set the
1267 					 * junk flag to indicate just ignore
1268 					 * any data upto the next CRLF.
1269 					 */
1270 					f->ftps_junk = 1;
1271 					continue;
1272 				}
1273 			}
1274 		}
1275 
1276 		while ((f->ftps_junk == 0) && (wptr > rptr)) {
1277 			len = wptr - rptr;
1278 			f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len, ifsftp);
1279 
1280 			if (ifsftp->ippr_ftp_debug > 3) {
1281 				printf("%s=%d len %d rv %d ptr %lx/%lx ",
1282 				       "ippr_ftp_valid",
1283 				       f->ftps_junk, len, rv, (u_long)rptr,
1284 				       (u_long)wptr);
1285 				printf("buf [%*s]\n", len, rptr);
1286 			}
1287 
1288 			if (f->ftps_junk == 0) {
1289 				f->ftps_rptr = rptr;
1290 				if (rv)
1291 					inc += ippr_ftp_server(fin, ip, nat,
1292 							       ftp, len, ifsftp);
1293 				else
1294 					inc += ippr_ftp_client(fin, ip, nat,
1295 							       ftp, len, ifsftp);
1296 				rptr = f->ftps_rptr;
1297 				wptr = f->ftps_wptr;
1298 			}
1299 		}
1300 
1301 		/*
1302 		 * Off to a bad start so lets just forget about using the
1303 		 * ftp proxy for this connection.
1304 		 */
1305 		if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) {
1306 			/* f->ftps_seq[1] += inc; */
1307 
1308 			if (ifsftp->ippr_ftp_debug > 1)
1309 				printf("%s:cmds == 0 junk == 1\n",
1310 				       "ippr_ftp_process");
1311 			return APR_ERR(2);
1312 		}
1313 
1314 		if ((f->ftps_junk != 0) && (rptr < wptr)) {
1315 			for (s = rptr; s < wptr; s++) {
1316 				if ((*s == '\r') && (s + 1 < wptr) &&
1317 				    (*(s + 1) == '\n')) {
1318 					rptr = s + 2;
1319 					f->ftps_junk = 0;
1320 					break;
1321 				}
1322 			}
1323 		}
1324 
1325 		if (rptr == wptr) {
1326 			rptr = wptr = f->ftps_buf;
1327 		} else {
1328 			/*
1329 			 * Compact the buffer back to the start.  The junk
1330 			 * flag should already be set and because we're not
1331 			 * throwing away any data, it is preserved from its
1332 			 * current state.
1333 			 */
1334 			if (rptr > f->ftps_buf) {
1335 				bcopy(rptr, f->ftps_buf, len);
1336 				wptr -= rptr - f->ftps_buf;
1337 				rptr = f->ftps_buf;
1338 			}
1339 		}
1340 		f->ftps_rptr = rptr;
1341 		f->ftps_wptr = wptr;
1342 	}
1343 
1344 	/* f->ftps_seq[1] += inc; */
1345 	if (tcp->th_flags & TH_FIN)
1346 		f->ftps_seq[1]++;
1347 	if (ifsftp->ippr_ftp_debug > 3) {
1348 #ifdef __sgi
1349 		mlen = fin->fin_plen;
1350 #else
1351 		mlen = MSGDSIZE(m);
1352 #endif
1353 		mlen -= off;
1354 		printf("ftps_seq[1] = %x inc %d len %d\n",
1355 		       f->ftps_seq[1], inc, mlen);
1356 	}
1357 
1358 	f->ftps_rptr = rptr;
1359 	f->ftps_wptr = wptr;
1360 	return APR_INC(inc);
1361 }
1362 
1363 
1364 int ippr_ftp_out(fin, aps, nat, private)
1365 fr_info_t *fin;
1366 ap_session_t *aps;
1367 nat_t *nat;
1368 void *private;
1369 {
1370 	ftpinfo_t *ftp;
1371 	int rev;
1372 
1373 	ftp = aps->aps_data;
1374 	if (ftp == NULL)
1375 		return 0;
1376 
1377 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1378 	if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1379 		ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1380 
1381 	return ippr_ftp_process(fin, nat, ftp, rev, (ifs_ftppxy_t *)private);
1382 }
1383 
1384 
1385 int ippr_ftp_in(fin, aps, nat, private)
1386 fr_info_t *fin;
1387 ap_session_t *aps;
1388 nat_t *nat;
1389 void *private;
1390 {
1391 	ftpinfo_t *ftp;
1392 	int rev;
1393 
1394 	ftp = aps->aps_data;
1395 	if (ftp == NULL)
1396 		return 0;
1397 
1398 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1399 	if (ftp->ftp_side[rev].ftps_ifp == NULL)
1400 		ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1401 
1402 	return ippr_ftp_process(fin, nat, ftp, 1 - rev, (ifs_ftppxy_t *)private);
1403 }
1404 
1405 
1406 /*
1407  * ippr_ftp_atoi - implement a version of atoi which processes numbers in
1408  * pairs separated by commas (which are expected to be in the range 0 - 255),
1409  * returning a 16 bit number combining either side of the , as the MSB and
1410  * LSB.
1411  */
1412 u_short ippr_ftp_atoi(ptr)
1413 char **ptr;
1414 {
1415 	register char *s = *ptr, c;
1416 	register u_char i = 0, j = 0;
1417 
1418 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1419 		i *= 10;
1420 		i += c - '0';
1421 	}
1422 	if (c != ',') {
1423 		*ptr = NULL;
1424 		return 0;
1425 	}
1426 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
1427 		j *= 10;
1428 		j += c - '0';
1429 	}
1430 	*ptr = s;
1431 	i &= 0xff;
1432 	j &= 0xff;
1433 	return (i << 8) | j;
1434 }
1435 
1436 
1437 int ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp)
1438 fr_info_t *fin;
1439 ip_t *ip;
1440 nat_t *nat;
1441 ftpside_t *f;
1442 int dlen;
1443 ifs_ftppxy_t *ifsftp;
1444 {
1445 	char newbuf[IPF_FTPBUFSZ];
1446 	char *s;
1447 	u_short ap = 0;
1448 
1449 #define EPSV_REPLEN	33
1450 	/*
1451 	 * Check for EPSV reply message.
1452 	 */
1453 	if (dlen < IPF_MIN229LEN)
1454 		return (0);
1455 	else if (strncmp(f->ftps_rptr,
1456 			 "229 Entering Extended Passive Mode", EPSV_REPLEN))
1457 		return (0);
1458 
1459 	/*
1460 	 * Skip the EPSV command + space
1461 	 */
1462 	s = f->ftps_rptr + 33;
1463 	while (*s && !ISDIGIT(*s))
1464 		s++;
1465 
1466 	/*
1467 	 * As per RFC 2428, there are no addres components in the EPSV
1468 	 * response.  So we'll go straight to getting the port.
1469 	 */
1470 	while (*s && ISDIGIT(*s)) {
1471 		ap *= 10;
1472 		ap += *s++ - '0';
1473 	}
1474 
1475 	if (!s)
1476 		return 0;
1477 
1478 	if (*s == '|')
1479 		s++;
1480 	if (*s == ')')
1481 		s++;
1482 	if (*s == '\n')
1483 		s--;
1484 	/*
1485 	 * check for CR-LF at the end.
1486 	 */
1487 	if ((*s == '\r') && (*(s + 1) == '\n')) {
1488 		s += 2;
1489 	} else
1490 		return 0;
1491 
1492 #if defined(SNPRINTF) && defined(_KERNEL)
1493 	(void) SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1494 		 "229 Entering Extended Passive Mode", ap);
1495 #else
1496 	(void) sprintf(newbuf, "%s (|||%u|)\r\n",
1497 		       "229 Entering Extended Passive Mode", ap);
1498 #endif
1499 
1500 	return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s,
1501 				  ip->ip_src.s_addr, ifsftp);
1502 }
1503