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