ip_ftp_pxy.c revision 72006
1/*
2 * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
3 * code.
4 *
5 * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 72006 2001-02-04 14:26:56Z darrenr $
6 */
7#if SOLARIS && defined(_KERNEL)
8extern	kmutex_t	ipf_rw;
9#endif
10
11#define	isdigit(x)	((x) >= '0' && (x) <= '9')
12#define	isupper(x)	(((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
13#define	islower(x)	(((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
14#define	isalpha(x)	(isupper(x) || islower(x))
15#define	toupper(x)	(isupper(x) ? (x) : (x) - 'a' + 'A')
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_FTPBUFSZ	96	/* This *MUST* be >= 53! */
24
25
26int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
27int ippr_ftp_complete __P((char *, size_t));
28int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
29int ippr_ftp_init __P((void));
30int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
31int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
32int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
33int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
34int ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
35int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
36int ippr_ftp_valid __P((char *, size_t));
37u_short ippr_ftp_atoi __P((char **));
38
39static	frentry_t	natfr;
40int	ippr_ftp_pasvonly = 0;
41int	ippr_ftp_insecure = 0;
42
43
44/*
45 * Initialize local structures.
46 */
47int ippr_ftp_init()
48{
49	bzero((char *)&natfr, sizeof(natfr));
50	natfr.fr_ref = 1;
51	natfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
52	return 0;
53}
54
55
56int ippr_ftp_new(fin, ip, aps, nat)
57fr_info_t *fin;
58ip_t *ip;
59ap_session_t *aps;
60nat_t *nat;
61{
62	ftpinfo_t *ftp;
63	ftpside_t *f;
64
65	KMALLOC(ftp, ftpinfo_t *);
66	if (ftp == NULL)
67		return -1;
68	aps->aps_data = ftp;
69	aps->aps_psiz = sizeof(ftpinfo_t);
70
71	bzero((char *)ftp, sizeof(*ftp));
72	f = &ftp->ftp_side[0];
73	f->ftps_rptr = f->ftps_buf;
74	f->ftps_wptr = f->ftps_buf;
75	f = &ftp->ftp_side[1];
76	f->ftps_rptr = f->ftps_buf;
77	f->ftps_wptr = f->ftps_buf;
78	return 0;
79}
80
81
82int ippr_ftp_port(fin, ip, nat, f, dlen)
83fr_info_t *fin;
84ip_t *ip;
85nat_t *nat;
86ftpside_t *f;
87int dlen;
88{
89	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
90	char newbuf[IPF_FTPBUFSZ], *s;
91	u_short a5, a6, sp, dp;
92	u_int a1, a2, a3, a4;
93	struct in_addr swip;
94	size_t nlen, olen;
95	fr_info_t fi;
96	int inc, off;
97	nat_t *ipn;
98	mb_t *m;
99#if	SOLARIS
100	mb_t *m1;
101#endif
102
103	tcp = (tcphdr_t *)fin->fin_dp;
104	/*
105	 * Check for client sending out PORT message.
106	 */
107	if (dlen < IPF_MINPORTLEN)
108		return 0;
109	off = fin->fin_hlen + (tcp->th_off << 2);
110	/*
111	 * Skip the PORT command + space
112	 */
113	s = f->ftps_rptr + 5;
114	/*
115	 * Pick out the address components, two at a time.
116	 */
117	a1 = ippr_ftp_atoi(&s);
118	if (!s)
119		return 0;
120	a2 = ippr_ftp_atoi(&s);
121	if (!s)
122		return 0;
123	/*
124	 * check that IP address in the PORT/PASV reply is the same as the
125	 * sender of the command - prevents using PORT for port scanning.
126	 */
127	a1 <<= 16;
128	a1 |= a2;
129	if (a1 != ntohl(nat->nat_inip.s_addr))
130		return 0;
131
132	a5 = ippr_ftp_atoi(&s);
133	if (!s)
134		return 0;
135	if (*s == ')')
136		s++;
137
138	/*
139	 * check for CR-LF at the end.
140	 */
141	if (*s == '\n')
142		s--;
143	if ((*s == '\r') && (*(s + 1) == '\n')) {
144		s += 2;
145		a6 = a5 & 0xff;
146	} else
147		return 0;
148	a5 >>= 8;
149	a5 &= 0xff;
150	/*
151	 * Calculate new address parts for PORT command
152	 */
153	a1 = ntohl(ip->ip_src.s_addr);
154	a2 = (a1 >> 16) & 0xff;
155	a3 = (a1 >> 8) & 0xff;
156	a4 = a1 & 0xff;
157	a1 >>= 24;
158	olen = s - f->ftps_rptr;
159	/* DO NOT change this to sprintf! */
160	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
161		       "PORT", a1, a2, a3, a4, a5, a6);
162
163	nlen = strlen(newbuf);
164	inc = nlen - olen;
165	if ((inc + ip->ip_len) > 65535)
166		return 0;
167
168#if SOLARIS
169	m = fin->fin_qfm;
170	for (m1 = m; m1->b_cont; m1 = m1->b_cont)
171		;
172	if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
173		mblk_t *nm;
174
175		/* alloc enough to keep same trailer space for lower driver */
176		nm = allocb(nlen, BPRI_MED);
177		PANIC((!nm),("ippr_ftp_out: allocb failed"));
178
179		nm->b_band = m1->b_band;
180		nm->b_wptr += nlen;
181
182		m1->b_wptr -= olen;
183		PANIC((m1->b_wptr < m1->b_rptr),
184		      ("ippr_ftp_out: cannot handle fragmented data block"));
185
186		linkb(m1, nm);
187	} else {
188		if (m1->b_datap->db_struiolim == m1->b_wptr)
189			m1->b_datap->db_struiolim += inc;
190		m1->b_datap->db_struioflag &= ~STRUIO_IP;
191		m1->b_wptr += inc;
192	}
193	copyin_mblk(m, off, nlen, newbuf);
194#else
195	m = *((mb_t **)fin->fin_mp);
196	if (inc < 0)
197		m_adj(m, inc);
198	/* the mbuf chain will be extended if necessary by m_copyback() */
199	m_copyback(m, off, nlen, newbuf);
200# ifdef	M_PKTHDR
201	if (!(m->m_flags & M_PKTHDR))
202		m->m_pkthdr.len += inc;
203# endif
204#endif
205	if (inc != 0) {
206#if SOLARIS || defined(__sgi)
207		register u_32_t	sum1, sum2;
208
209		sum1 = ip->ip_len;
210		sum2 = ip->ip_len + inc;
211
212		/* Because ~1 == -2, We really need ~1 == -1 */
213		if (sum1 > sum2)
214			sum2--;
215		sum2 -= sum1;
216		sum2 = (sum2 & 0xffff) + (sum2 >> 16);
217
218		fix_outcksum(&ip->ip_sum, sum2);
219#endif
220		ip->ip_len += inc;
221	}
222
223	/*
224	 * Add skeleton NAT entry for connection which will come back the
225	 * other way.
226	 */
227	sp = htons(a5 << 8 | a6);
228	/*
229	 * Don't allow the PORT command to specify a port < 1024 due to
230	 * security crap.
231	 */
232	if (ntohs(sp) < 1024)
233		return 0;
234	/*
235	 * The server may not make the connection back from port 20, but
236	 * it is the most likely so use it here to check for a conflicting
237	 * mapping.
238	 */
239	dp = htons(fin->fin_data[1] - 1);
240	ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
241			    ip->ip_dst, (dp << 16) | sp, 0);
242	if (ipn == NULL) {
243		int slen;
244
245		slen = ip->ip_len;
246		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
247		bcopy((char *)fin, (char *)&fi, sizeof(fi));
248		bzero((char *)tcp2, sizeof(*tcp2));
249		tcp2->th_win = htons(8192);
250		tcp2->th_sport = sp;
251		tcp2->th_off = 5;
252		tcp2->th_dport = 0; /* XXX - don't specify remote port */
253		fi.fin_data[0] = ntohs(sp);
254		fi.fin_data[1] = 0;
255		fi.fin_dlen = sizeof(*tcp2);
256		fi.fin_dp = (char *)tcp2;
257		fi.fin_fr = &natfr;
258		swip = ip->ip_src;
259		fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
260		ip->ip_src = nat->nat_inip;
261		ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT,
262			      NAT_OUTBOUND);
263		if (ipn != NULL) {
264			ipn->nat_age = fr_defnatage;
265			(void) fr_addstate(ip, &fi, FI_W_DPORT);
266		}
267		ip->ip_len = slen;
268		ip->ip_src = swip;
269	}
270	return APR_INC(inc);
271}
272
273
274int ippr_ftp_client(fin, ip, nat, ftp, dlen)
275fr_info_t *fin;
276nat_t *nat;
277ftpinfo_t *ftp;
278ip_t *ip;
279int dlen;
280{
281	char *rptr, *wptr, cmd[6], c;
282	ftpside_t *f;
283	int inc, i;
284
285	inc = 0;
286	f = &ftp->ftp_side[0];
287	rptr = f->ftps_rptr;
288	wptr = f->ftps_wptr;
289
290	for (i = 0; (i < 5) && (i < dlen); i++) {
291		c = rptr[i];
292		if (isalpha(c)) {
293			cmd[i] = toupper(c);
294		} else {
295			cmd[i] = c;
296		}
297	}
298	cmd[i] = '\0';
299
300	if ((ftp->ftp_passok == 0) && !strncmp(cmd, "USER ", 5))
301		 ftp->ftp_passok = 1;
302	else if ((ftp->ftp_passok == 2) && !strncmp(cmd, "PASS ", 5))
303		 ftp->ftp_passok = 3;
304	else if ((ftp->ftp_passok == 4) && !ippr_ftp_pasvonly &&
305		 !strncmp(cmd, "PORT ", 5)) {
306		inc = ippr_ftp_port(fin, ip, nat, f, dlen);
307	} else if (ippr_ftp_insecure && !ippr_ftp_pasvonly &&
308		   !strncmp(cmd, "PORT ", 5)) {
309		inc = ippr_ftp_port(fin, ip, nat, f, dlen);
310	}
311
312	while ((*rptr++ != '\n') && (rptr < wptr))
313		;
314	f->ftps_rptr = rptr;
315	return inc;
316}
317
318
319int ippr_ftp_pasv(fin, ip, nat, f, dlen)
320fr_info_t *fin;
321ip_t *ip;
322nat_t *nat;
323ftpside_t *f;
324int dlen;
325{
326	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
327	struct in_addr swip, swip2;
328	u_short a5, a6, sp, dp;
329	u_int a1, a2, a3, a4;
330	fr_info_t fi;
331	nat_t *ipn;
332	int inc;
333	char *s;
334
335	/*
336	 * Check for PASV reply message.
337	 */
338	if (dlen < IPF_MIN227LEN)
339		return 0;
340	else if (strncmp(f->ftps_rptr, "227 Entering Passive Mode", 25))
341		return 0;
342
343	tcp = (tcphdr_t *)fin->fin_dp;
344
345	/*
346	 * Skip the PORT command + space
347	 */
348	s = f->ftps_rptr + 25;
349	while (*s && !isdigit(*s))
350		s++;
351	/*
352	 * Pick out the address components, two at a time.
353	 */
354	a1 = ippr_ftp_atoi(&s);
355	if (!s)
356		return 0;
357	a2 = ippr_ftp_atoi(&s);
358	if (!s)
359		return 0;
360
361	/*
362	 * check that IP address in the PORT/PASV reply is the same as the
363	 * sender of the command - prevents using PORT for port scanning.
364	 */
365	a1 <<= 16;
366	a1 |= a2;
367	if (a1 != ntohl(nat->nat_oip.s_addr))
368		return 0;
369
370	a5 = ippr_ftp_atoi(&s);
371	if (!s)
372		return 0;
373
374	if (*s == ')')
375		s++;
376	if (*s == '\n')
377		s--;
378	/*
379	 * check for CR-LF at the end.
380	 */
381	if ((*s == '\r') && (*(s + 1) == '\n')) {
382		s += 2;
383		a6 = a5 & 0xff;
384	} else
385		return 0;
386	a5 >>= 8;
387	/*
388	 * Calculate new address parts for 227 reply
389	 */
390	a1 = ntohl(ip->ip_src.s_addr);
391	a2 = (a1 >> 16) & 0xff;
392	a3 = (a1 >> 8) & 0xff;
393	a4 = a1 & 0xff;
394	a1 >>= 24;
395	inc = 0;
396#if 0
397	olen = s - f->ftps_rptr;
398	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
399		       "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
400	nlen = strlen(newbuf);
401	inc = nlen - olen;
402	if ((inc + ip->ip_len) > 65535)
403		return 0;
404
405#if SOLARIS
406	m = fin->fin_qfm;
407	for (m1 = m; m1->b_cont; m1 = m1->b_cont)
408		;
409	if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
410		mblk_t *nm;
411
412		/* alloc enough to keep same trailer space for lower driver */
413		nm = allocb(nlen, BPRI_MED);
414		PANIC((!nm),("ippr_ftp_out: allocb failed"));
415
416		nm->b_band = m1->b_band;
417		nm->b_wptr += nlen;
418
419		m1->b_wptr -= olen;
420		PANIC((m1->b_wptr < m1->b_rptr),
421		      ("ippr_ftp_out: cannot handle fragmented data block"));
422
423		linkb(m1, nm);
424	} else {
425		m1->b_wptr += inc;
426	}
427	/*copyin_mblk(m, off, nlen, newbuf);*/
428#else /* SOLARIS */
429	m = *((mb_t **)fin->fin_mp);
430	if (inc < 0)
431		m_adj(m, inc);
432	/* the mbuf chain will be extended if necessary by m_copyback() */
433	/*m_copyback(m, off, nlen, newbuf);*/
434#endif /* SOLARIS */
435	if (inc != 0) {
436#if SOLARIS || defined(__sgi)
437		register u_32_t	sum1, sum2;
438
439		sum1 = ip->ip_len;
440		sum2 = ip->ip_len + inc;
441
442		/* Because ~1 == -2, We really need ~1 == -1 */
443		if (sum1 > sum2)
444			sum2--;
445		sum2 -= sum1;
446		sum2 = (sum2 & 0xffff) + (sum2 >> 16);
447
448		fix_outcksum(&ip->ip_sum, sum2);
449#endif /* SOLARIS || defined(__sgi) */
450		ip->ip_len += inc;
451	}
452#endif /* 0 */
453
454	/*
455	 * Add skeleton NAT entry for connection which will come back the
456	 * other way.
457	 */
458	sp = 0;
459	dp = htons(fin->fin_data[1] - 1);
460	ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
461			    ip->ip_dst, (dp << 16) | sp, 0);
462	if (ipn == NULL) {
463		int slen;
464
465		slen = ip->ip_len;
466		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
467		bcopy((char *)fin, (char *)&fi, sizeof(fi));
468		bzero((char *)tcp2, sizeof(*tcp2));
469		tcp2->th_win = htons(8192);
470		tcp2->th_sport = 0;		/* XXX - fake it for nat_new */
471		tcp2->th_off = 5;
472		fi.fin_data[1] = a5 << 8 | a6;
473		fi.fin_dlen = sizeof(*tcp2);
474		tcp2->th_dport = htons(fi.fin_data[1]);
475		fi.fin_data[0] = 0;
476		fi.fin_dp = (char *)tcp2;
477		fi.fin_fr = &natfr;
478		swip = ip->ip_src;
479		swip2 = ip->ip_dst;
480		fi.fin_fi.fi_daddr = ip->ip_src.s_addr;
481		fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
482		ip->ip_dst = ip->ip_src;
483		ip->ip_src = nat->nat_inip;
484		ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_SPORT,
485			      NAT_OUTBOUND);
486		if (ipn != NULL) {
487			ipn->nat_age = fr_defnatage;
488			(void) fr_addstate(ip, &fi, FI_W_SPORT);
489		}
490		ip->ip_len = slen;
491		ip->ip_src = swip;
492		ip->ip_dst = swip2;
493	}
494	return inc;
495}
496
497
498int ippr_ftp_server(fin, ip, nat, ftp, dlen)
499fr_info_t *fin;
500ip_t *ip;
501nat_t *nat;
502ftpinfo_t *ftp;
503int dlen;
504{
505	char *rptr, *wptr;
506	ftpside_t *f;
507	int inc;
508
509	inc = 0;
510	f = &ftp->ftp_side[1];
511	rptr = f->ftps_rptr;
512	wptr = f->ftps_wptr;
513
514	if ((ftp->ftp_passok == 1) && !strncmp(rptr, "331", 3))
515		 ftp->ftp_passok = 2;
516	else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "230", 3))
517		 ftp->ftp_passok = 4;
518	else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "530", 3))
519		 ftp->ftp_passok = 0;
520	else if ((ftp->ftp_passok == 4) && !strncmp(rptr, "227 ", 4)) {
521		inc = ippr_ftp_pasv(fin, ip, nat, f, dlen);
522	} else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
523		inc = ippr_ftp_pasv(fin, ip, nat, f, dlen);
524	}
525	while ((*rptr++ != '\n') && (rptr < wptr))
526		;
527	f->ftps_rptr = rptr;
528	return inc;
529}
530
531
532/*
533 * Look to see if the buffer starts with something which we recognise as
534 * being the correct syntax for the FTP protocol.
535 */
536int ippr_ftp_valid(buf, len)
537char *buf;
538size_t len;
539{
540	register char *s, c;
541	register size_t i = len;
542
543	if (i < 5)
544		return 2;
545	s = buf;
546	c = *s++;
547	i--;
548
549	if (isdigit(c)) {
550		c = *s++;
551		i--;
552		if (isdigit(c)) {
553			c = *s++;
554			i--;
555			if (isdigit(c)) {
556				c = *s++;
557				i--;
558				if ((c != '-') && (c != ' '))
559					return 1;
560			} else
561				return 1;
562		} else
563			return 1;
564	} else if (isalpha(c)) {
565		c = *s++;
566		i--;
567		if (isalpha(c)) {
568			c = *s++;
569			i--;
570			if (isalpha(c)) {
571				c = *s++;
572				i--;
573				if (isalpha(c)) {
574					c = *s++;
575					i--;
576					if ((c != ' ') && (c != '\r'))
577						return 1;
578				} else if ((c != ' ') && (c != '\r'))
579					return 1;
580			} else
581				return 1;
582		} else
583			return 1;
584	} else
585		return 1;
586	for (; i; i--) {
587		c = *s++;
588		if (c == '\n')
589			return 0;
590	}
591	return 2;
592}
593
594
595int ippr_ftp_process(fin, ip, nat, ftp, rv)
596fr_info_t *fin;
597ip_t *ip;
598nat_t *nat;
599ftpinfo_t *ftp;
600int rv;
601{
602	int mlen, len, off, inc, i, sel;
603	char *rptr, *wptr;
604	ftpside_t *f, *t;
605	tcphdr_t *tcp;
606	mb_t *m;
607
608	tcp = (tcphdr_t *)fin->fin_dp;
609	off = fin->fin_hlen + (tcp->th_off << 2);
610
611#if	SOLARIS
612	m = fin->fin_qfm;
613#else
614	m = *((mb_t **)fin->fin_mp);
615#endif
616
617#if	SOLARIS
618	mlen = msgdsize(m) - off;
619#else
620	mlen = mbufchainlen(m) - off;
621#endif
622
623	t = &ftp->ftp_side[1 - rv];
624	f = &ftp->ftp_side[rv];
625	if (!mlen) {
626		if (!t->ftps_seq ||
627		    (int)ntohl(tcp->th_ack) - (int)t->ftps_seq > 0)
628			t->ftps_seq = ntohl(tcp->th_ack);
629		f->ftps_len = 0;
630		return 0;
631	}
632
633	inc = 0;
634	rptr = f->ftps_rptr;
635	wptr = f->ftps_wptr;
636
637	sel = nat->nat_aps->aps_sel[1 - rv];
638	if (rv)
639		i = nat->nat_aps->aps_ackoff[sel];
640	else
641		i = nat->nat_aps->aps_seqoff[sel];
642	/*
643	 * XXX - Ideally, this packet should get dropped because we now know
644	 * that it is out of order (and there is no real danger in doing so
645	 * apart from causing packets to go through here ordered).
646	 */
647	if (f->ftps_len + f->ftps_seq == ntohl(tcp->th_seq))
648		f->ftps_seq = ntohl(tcp->th_seq);
649	else if (ntohl(tcp->th_seq) + i != f->ftps_seq) {
650		return APR_ERR(-1);
651	}
652	f->ftps_len = mlen;
653
654	while (mlen > 0) {
655		len = MIN(mlen, FTP_BUFSZ / 2);
656
657#if	SOLARIS
658		copyout_mblk(m, off, len, wptr);
659#else
660		m_copydata(m, off, len, wptr);
661#endif
662		mlen -= len;
663		off += len;
664		wptr += len;
665		f->ftps_wptr = wptr;
666		if (f->ftps_junk == 2)
667			f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr);
668
669		while ((f->ftps_junk == 0) && (wptr > rptr)) {
670			f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr);
671			if (f->ftps_junk == 0) {
672				len = wptr - rptr;
673				f->ftps_rptr = rptr;
674				if (rv)
675					inc += ippr_ftp_server(fin, ip, nat,
676							       ftp, len);
677				else
678					inc += ippr_ftp_client(fin, ip, nat,
679							       ftp, len);
680				rptr = f->ftps_rptr;
681			}
682		}
683
684		while ((f->ftps_junk == 1) && (rptr < wptr)) {
685			while ((rptr < wptr) && (*rptr != '\r'))
686				rptr++;
687
688			if (*rptr == '\r') {
689				if (rptr + 1 < wptr) {
690					if (*(rptr + 1) == '\n') {
691						rptr += 2;
692						f->ftps_junk = 0;
693					} else
694						rptr++;
695				} else
696					break;
697			}
698		}
699		f->ftps_rptr = rptr;
700
701		if (rptr == wptr) {
702			rptr = wptr = f->ftps_buf;
703		} else {
704			if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) {
705				i = wptr - rptr;
706				if ((rptr == f->ftps_buf) ||
707				    (wptr - rptr > FTP_BUFSZ / 2)) {
708					f->ftps_junk = 1;
709					rptr = wptr = f->ftps_buf;
710				} else {
711					bcopy(rptr, f->ftps_buf, i);
712					wptr = f->ftps_buf + i;
713					rptr = f->ftps_buf;
714				}
715			}
716			f->ftps_rptr = rptr;
717			f->ftps_wptr = wptr;
718		}
719	}
720
721	t->ftps_seq = ntohl(tcp->th_ack);
722	f->ftps_rptr = rptr;
723	f->ftps_wptr = wptr;
724	return APR_INC(inc);
725}
726
727
728int ippr_ftp_out(fin, ip, aps, nat)
729fr_info_t *fin;
730ip_t *ip;
731ap_session_t *aps;
732nat_t *nat;
733{
734	ftpinfo_t *ftp;
735
736	ftp = aps->aps_data;
737	if (ftp == NULL)
738		return 0;
739	return ippr_ftp_process(fin, ip, nat, ftp, 0);
740}
741
742
743int ippr_ftp_in(fin, ip, aps, nat)
744fr_info_t *fin;
745ip_t *ip;
746ap_session_t *aps;
747nat_t *nat;
748{
749	ftpinfo_t *ftp;
750
751	ftp = aps->aps_data;
752	if (ftp == NULL)
753		return 0;
754	return ippr_ftp_process(fin, ip, nat, ftp, 1);
755}
756
757
758/*
759 * ippr_ftp_atoi - implement a version of atoi which processes numbers in
760 * pairs separated by commas (which are expected to be in the range 0 - 255),
761 * returning a 16 bit number combining either side of the , as the MSB and
762 * LSB.
763 */
764u_short ippr_ftp_atoi(ptr)
765char **ptr;
766{
767	register char *s = *ptr, c;
768	register u_char i = 0, j = 0;
769
770	while ((c = *s++) && isdigit(c)) {
771		i *= 10;
772		i += c - '0';
773	}
774	if (c != ',') {
775		*ptr = NULL;
776		return 0;
777	}
778	while ((c = *s++) && isdigit(c)) {
779		j *= 10;
780		j += c - '0';
781	}
782	*ptr = s;
783	i &= 0xff;
784	j &= 0xff;
785	return (i << 8) | j;
786}
787