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