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