Home | History | Annotate | Line # | Download | only in netinet
ip_ftp_pxy.c revision 1.1
      1 /*	$NetBSD: ip_ftp_pxy.c,v 1.1 2012/03/23 20:36:56 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2011 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  * Id
     12  */
     13 
     14 #define	IPF_FTP_PROXY
     15 
     16 #define	IPF_MINPORTLEN	18
     17 #define	IPF_MINEPRTLEN	20
     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 #define	FTPXY_JUNK_OK	0
     42 #define	FTPXY_JUNK_BAD	1	/* Ignore all commands for this connection */
     43 #define	FTPXY_JUNK_EOL	2	/* consume the rest of this line only */
     44 #define	FTPXY_JUNK_CONT	3	/* Saerching for next numeric */
     45 
     46 /*
     47  * Values for FTP commands.  Numerics cover 0-999
     48  */
     49 #define	FTPXY_C_PASV	1000
     50 #define	FTPXY_C_PORT	1001
     51 #define	FTPXY_C_EPSV	1002
     52 #define	FTPXY_C_EPRT	1003
     53 
     54 
     55 typedef struct ipf_ftp_softc_s {
     56 	int	ipf_p_ftp_pasvonly;
     57 	/* Do not require logins before transfers */
     58 	int	ipf_p_ftp_insecure;
     59 	int	ipf_p_ftp_pasvrdr;
     60 	/* PASV must be last command prior to 227 */
     61 	int	ipf_p_ftp_forcepasv;
     62 	int	ipf_p_ftp_debug;
     63 	int	ipf_p_ftp_single_xfer;
     64 	void	*ipf_p_ftp_tune;
     65 } ipf_ftp_softc_t;
     66 
     67 
     68 void ipf_p_ftp_main_load __P((void));
     69 void ipf_p_ftp_main_unload __P((void));
     70 void *ipf_p_ftp_soft_create __P((ipf_main_softc_t *));
     71 void ipf_p_ftp_soft_destroy __P((ipf_main_softc_t *, void *));
     72 
     73 int ipf_p_ftp_client __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
     74 			  ftpinfo_t *, int));
     75 int ipf_p_ftp_complete __P((char *, size_t));
     76 int ipf_p_ftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *));
     77 int ipf_p_ftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *));
     78 void ipf_p_ftp_del __P((ipf_main_softc_t *, ap_session_t *));
     79 int ipf_p_ftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *));
     80 int ipf_p_ftp_pasv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
     81 			ftpinfo_t *, int));
     82 int ipf_p_ftp_epsv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
     83 			ftpinfo_t *, int));
     84 int ipf_p_ftp_port __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
     85 			ftpinfo_t *, int));
     86 int ipf_p_ftp_process __P((ipf_ftp_softc_t *, fr_info_t *, nat_t *,
     87 			   ftpinfo_t *, int));
     88 int ipf_p_ftp_server __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
     89 			  ftpinfo_t *, int));
     90 int ipf_p_ftp_valid __P((ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t));
     91 int ipf_p_ftp_server_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *,
     92 				size_t));
     93 int ipf_p_ftp_client_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *,
     94 				size_t));
     95 u_short ipf_p_ftp_atoi __P((char **));
     96 int ipf_p_ftp_pasvreply __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
     97 			     ftpinfo_t *, u_int, char *, char *, u_int));
     98 int ipf_p_ftp_eprt __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
     99 			ftpinfo_t *, int));
    100 int ipf_p_ftp_eprt4 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
    101 			 ftpinfo_t *, int));
    102 int ipf_p_ftp_addport __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
    103 			   ftpinfo_t *, int, int, int));
    104 void ipf_p_ftp_setpending __P((ipf_main_softc_t *, ftpinfo_t *));
    105 
    106 /*
    107  * Debug levels:
    108  * 1 - security
    109  * 2 - errors
    110  * 3 - error debugging
    111  * 4 - parsing errors
    112  * 5 - parsing info
    113  * 6 - parsing debug
    114  */
    115 
    116 static	int	ipf_p_ftp_proxy_init = 0;
    117 static	frentry_t	ftppxyfr;
    118 static	ipftuneable_t	ipf_ftp_tuneables[] = {
    119 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) },
    120 		"ftp_debug",	0,	10,
    121 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug),
    122 		0, NULL, NULL },
    123 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) },
    124 		"ftp_pasvonly",	0,	1,
    125 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly),
    126 		0, NULL, NULL },
    127 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) },
    128 		"ftp_insecure",	0,	1,
    129 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure),
    130 		0, NULL, NULL },
    131 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) },
    132 		"ftp_pasvrdr",	0,	1,
    133 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr),
    134 		0, NULL, NULL },
    135 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) },
    136 		"ftp_forcepasv", 0,	1,
    137 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv),
    138 		0, NULL, NULL },
    139 	{ { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) },
    140 		"ftp_single_xfer", 0,	1,
    141 		stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer),
    142 		0, NULL, NULL },
    143 	{ { NULL }, NULL, 0, 0, 0, 0, NULL, NULL }
    144 };
    145 
    146 
    147 void
    148 ipf_p_ftp_main_load()
    149 {
    150 	bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
    151 	ftppxyfr.fr_ref = 1;
    152 	ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
    153 
    154 	MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex");
    155 	ipf_p_ftp_proxy_init = 1;
    156 }
    157 
    158 
    159 void
    160 ipf_p_ftp_main_unload()
    161 {
    162 
    163 	if (ipf_p_ftp_proxy_init == 1) {
    164 		MUTEX_DESTROY(&ftppxyfr.fr_lock);
    165 		ipf_p_ftp_proxy_init = 0;
    166 	}
    167 }
    168 
    169 
    170 /*
    171  * Initialize local structures.
    172  */
    173 void *
    174 ipf_p_ftp_soft_create(softc)
    175 	ipf_main_softc_t *softc;
    176 {
    177 	ipf_ftp_softc_t *softf;
    178 
    179 	KMALLOC(softf, ipf_ftp_softc_t *);
    180 	if (softf == NULL)
    181 		return NULL;
    182 
    183 	bzero((char *)softf, sizeof(*softf));
    184 #if defined(_KERNEL)
    185 	softf->ipf_p_ftp_debug = 0;
    186 #else
    187 	softf->ipf_p_ftp_debug = 2;
    188 #endif
    189 	softf->ipf_p_ftp_forcepasv = 1;
    190 
    191 	softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf,
    192 						    sizeof(ipf_ftp_tuneables),
    193 						    ipf_ftp_tuneables);
    194 	if (softf->ipf_p_ftp_tune == NULL) {
    195 		ipf_p_ftp_soft_destroy(softc, softf);
    196 		return NULL;
    197 	}
    198 	if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) {
    199 		ipf_p_ftp_soft_destroy(softc, softf);
    200 		return NULL;
    201 	}
    202 
    203 	return softf;
    204 }
    205 
    206 
    207 void
    208 ipf_p_ftp_soft_destroy(softc, arg)
    209 	ipf_main_softc_t *softc;
    210 	void *arg;
    211 {
    212 	ipf_ftp_softc_t *softf = arg;
    213 
    214 	if (softf->ipf_p_ftp_tune != NULL) {
    215 		ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune);
    216 		KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables));
    217 		softf->ipf_p_ftp_tune = NULL;
    218 	}
    219 
    220 	KFREE(softf);
    221 }
    222 
    223 
    224 int
    225 ipf_p_ftp_new(arg, fin, aps, nat)
    226 	void *arg;
    227 	fr_info_t *fin;
    228 	ap_session_t *aps;
    229 	nat_t *nat;
    230 {
    231 	ftpinfo_t *ftp;
    232 	ftpside_t *f;
    233 
    234 	KMALLOC(ftp, ftpinfo_t *);
    235 	if (ftp == NULL)
    236 		return -1;
    237 
    238 	fin = fin;	/* LINT */
    239 	nat = nat;	/* LINT */
    240 
    241 	aps->aps_data = ftp;
    242 	aps->aps_psiz = sizeof(ftpinfo_t);
    243 
    244 	bzero((char *)ftp, sizeof(*ftp));
    245 	f = &ftp->ftp_side[0];
    246 	f->ftps_rptr = f->ftps_buf;
    247 	f->ftps_wptr = f->ftps_buf;
    248 	f = &ftp->ftp_side[1];
    249 	f->ftps_rptr = f->ftps_buf;
    250 	f->ftps_wptr = f->ftps_buf;
    251 	ftp->ftp_passok = FTPXY_INIT;
    252 	ftp->ftp_incok = 0;
    253 	return 0;
    254 }
    255 
    256 
    257 void
    258 ipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp)
    259 {
    260 	if (ftp->ftp_pendnat != NULL)
    261 		ipf_nat_setpending(softc, ftp->ftp_pendnat);
    262 
    263 	if (ftp->ftp_pendstate != NULL) {
    264 		READ_ENTER(&softc->ipf_state);
    265 		ipf_state_setpending(softc, ftp->ftp_pendstate);
    266 		RWLOCK_EXIT(&softc->ipf_state);
    267 	}
    268 }
    269 
    270 
    271 void
    272 ipf_p_ftp_del(softc, aps)
    273 	ipf_main_softc_t *softc;
    274 	ap_session_t *aps;
    275 {
    276 	ftpinfo_t *ftp;
    277 
    278 	ftp = aps->aps_data;
    279 	if (ftp != NULL)
    280 		ipf_p_ftp_setpending(softc, ftp);
    281 }
    282 
    283 
    284 int
    285 ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen)
    286 	ipf_ftp_softc_t *softf;
    287 	fr_info_t *fin;
    288 	ip_t *ip;
    289 	nat_t *nat;
    290 	ftpinfo_t *ftp;
    291 	int dlen;
    292 {
    293 	char newbuf[IPF_FTPBUFSZ], *s;
    294 	u_int a1, a2, a3, a4;
    295 	u_short a5, a6, sp;
    296 	size_t nlen, olen;
    297 	tcphdr_t *tcp;
    298 	int inc, off;
    299 	ftpside_t *f;
    300 	mb_t *m;
    301 
    302 	m = fin->fin_m;
    303 	f = &ftp->ftp_side[0];
    304 	tcp = (tcphdr_t *)fin->fin_dp;
    305 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
    306 
    307 	/*
    308 	 * Check for client sending out PORT message.
    309 	 */
    310 	if (dlen < IPF_MINPORTLEN) {
    311 		if (softf->ipf_p_ftp_debug > 1)
    312 			printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
    313 			       dlen);
    314 		return 0;
    315 	}
    316 	/*
    317 	 * Skip the PORT command + space
    318 	 */
    319 	s = f->ftps_rptr + 5;
    320 	/*
    321 	 * Pick out the address components, two at a time.
    322 	 */
    323 	a1 = ipf_p_ftp_atoi(&s);
    324 	if (s == NULL) {
    325 		if (softf->ipf_p_ftp_debug > 1)
    326 			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1);
    327 		return 0;
    328 	}
    329 	a2 = ipf_p_ftp_atoi(&s);
    330 	if (s == NULL) {
    331 		if (softf->ipf_p_ftp_debug > 1)
    332 			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2);
    333 		return 0;
    334 	}
    335 
    336 	/*
    337 	 * Check that IP address in the PORT/PASV reply is the same as the
    338 	 * sender of the command - prevents using PORT for port scanning.
    339 	 */
    340 	a1 <<= 16;
    341 	a1 |= a2;
    342 	if (((nat->nat_dir == NAT_OUTBOUND) &&
    343 	     (a1 != ntohl(nat->nat_osrcaddr))) ||
    344 	    ((nat->nat_dir == NAT_INBOUND) &&
    345 	     (a1 != ntohl(nat->nat_odstaddr)))) {
    346 		if (softf->ipf_p_ftp_debug > 0)
    347 			printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1");
    348 		return APR_ERR(1);
    349 	}
    350 
    351 	a5 = ipf_p_ftp_atoi(&s);
    352 	if (s == NULL) {
    353 		if (softf->ipf_p_ftp_debug > 1)
    354 			printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3);
    355 		return 0;
    356 	}
    357 	if (*s == ')')
    358 		s++;
    359 
    360 	/*
    361 	 * check for CR-LF at the end.
    362 	 */
    363 	if (*s == '\n')
    364 		s--;
    365 	if ((*s == '\r') && (*(s + 1) == '\n')) {
    366 		s += 2;
    367 		a6 = a5 & 0xff;
    368 	} else {
    369 		if (softf->ipf_p_ftp_debug > 1)
    370 			printf("ipf_p_ftp_port:missing %s\n", "cr-lf");
    371 		return 0;
    372 	}
    373 
    374 	a5 >>= 8;
    375 	a5 &= 0xff;
    376 	sp = a5 << 8 | a6;
    377 	/*
    378 	 * Don't allow the PORT command to specify a port < 1024 due to
    379 	 * security crap.
    380 	 */
    381 	if (sp < 1024) {
    382 		if (softf->ipf_p_ftp_debug > 0)
    383 			printf("ipf_p_ftp_port:sp(%d) < 1024\n", sp);
    384 		return 0;
    385 	}
    386 	/*
    387 	 * Calculate new address parts for PORT command
    388 	 */
    389 	if (nat->nat_dir == NAT_INBOUND)
    390 		a1 = ntohl(nat->nat_odstaddr);
    391 	else
    392 		a1 = ntohl(ip->ip_src.s_addr);
    393 	a2 = (a1 >> 16) & 0xff;
    394 	a3 = (a1 >> 8) & 0xff;
    395 	a4 = a1 & 0xff;
    396 	a1 >>= 24;
    397 	olen = s - f->ftps_rptr;
    398 	/* DO NOT change this to snprintf! */
    399 #if defined(SNPRINTF) && defined(_KERNEL)
    400 	SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
    401 		 "PORT", a1, a2, a3, a4, a5, a6);
    402 #else
    403 	(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
    404 		       "PORT", a1, a2, a3, a4, a5, a6);
    405 #endif
    406 
    407 	nlen = strlen(newbuf);
    408 	inc = nlen - olen;
    409 	if ((inc + fin->fin_plen) > 65535) {
    410 		if (softf->ipf_p_ftp_debug > 0)
    411 			printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n",
    412 			       inc);
    413 		return 0;
    414 	}
    415 
    416 #if !defined(_KERNEL)
    417 	bcopy(newbuf, MTOD(m, char *) + off, nlen);
    418 	m->mb_len += inc;
    419 #else
    420 	/*
    421 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
    422 	 * mean remove -len bytes from the end of the packet.
    423 	 * The mbuf chain will be extended if necessary by m_copyback().
    424 	 */
    425 	if (inc < 0)
    426 		M_ADJ(m, inc);
    427 #endif /* !defined(_KERNEL) */
    428 	COPYBACK(m, off, nlen, newbuf);
    429 
    430 	if (inc != 0) {
    431 		fin->fin_plen += inc;
    432 		ip->ip_len = htons(fin->fin_plen);
    433 		fin->fin_dlen += inc;
    434 	}
    435 
    436 	f->ftps_cmd = FTPXY_C_PORT;
    437 	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen,
    438 				 (a5 << 8) | a6, inc);
    439 }
    440 
    441 
    442 int
    443 ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, nport, inc)
    444 	ipf_ftp_softc_t *softf;
    445 	fr_info_t *fin;
    446 	ip_t *ip;
    447 	nat_t *nat;
    448 	ftpinfo_t *ftp;
    449 	int dlen, nport, inc;
    450 {
    451 	tcphdr_t tcph, *tcp2 = &tcph;
    452 	struct in_addr swip, swip2;
    453 	ipf_main_softc_t *softc;
    454 	ipf_nat_softc_t *softn;
    455 	fr_info_t fi;
    456 	nat_t *nat2;
    457 	u_short sp;
    458 	int flags;
    459 
    460 	softc = fin->fin_main_soft;
    461 	softn = softc->ipf_nat_soft;
    462 
    463 	if ((ftp->ftp_pendnat != NULL)  || (ftp->ftp_pendstate != NULL)) {
    464 		if (softf->ipf_p_ftp_single_xfer != 0) {
    465 			if (softf->ipf_p_ftp_debug > 0)
    466 				printf("ipf_p_ftp_addport:xfer active %p/%p\n",
    467 				       ftp->ftp_pendnat, ftp->ftp_pendstate);
    468 			return 0;
    469 		}
    470 		ipf_p_ftp_setpending(softc, ftp);
    471 	}
    472 
    473 	/*
    474 	 * Add skeleton NAT entry for connection which will come back the
    475 	 * other way.
    476 	 */
    477 	sp = nport;
    478 	/*
    479 	 * Don't allow the PORT command to specify a port < 1024 due to
    480 	 * security crap.
    481 	 */
    482 	if (sp < 1024) {
    483 		if (softf->ipf_p_ftp_debug > 0)
    484 			printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp);
    485 		return 0;
    486 	}
    487 	/*
    488 	 * The server may not make the connection back from port 20, but
    489 	 * it is the most likely so use it here to check for a conflicting
    490 	 * mapping.
    491 	 */
    492 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
    493 	fi.fin_flx |= FI_IGNORE;
    494 	fi.fin_data[0] = sp;
    495 	fi.fin_data[1] = fin->fin_data[1] - 1;
    496 	/*
    497 	 * Add skeleton NAT entry for connection which will come back the
    498 	 * other way.
    499 	 */
    500 	if (nat->nat_dir == NAT_OUTBOUND)
    501 		nat2 = ipf_nat_outlookup(&fi, NAT_SEARCH|IPN_TCP,
    502 					 nat->nat_pr[1], nat->nat_osrcip,
    503 					 nat->nat_odstip);
    504 	else
    505 		nat2 = ipf_nat_inlookup(&fi, NAT_SEARCH|IPN_TCP,
    506 					nat->nat_pr[0], nat->nat_nsrcip,
    507 					nat->nat_odstip);
    508 	if (nat2 == NULL) {
    509 		int slen;
    510 
    511 		slen = ip->ip_len;
    512 		ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp2));
    513 		bzero((char *)tcp2, sizeof(*tcp2));
    514 		tcp2->th_win = htons(8192);
    515 		tcp2->th_sport = htons(sp);
    516 		TCP_OFF_A(tcp2, 5);
    517 		tcp2->th_flags = TH_SYN;
    518 		tcp2->th_dport = 0; /* XXX - don't specify remote port */
    519 		fi.fin_data[1] = 0;
    520 		fi.fin_dlen = sizeof(*tcp2);
    521 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
    522 		fi.fin_dp = (char *)tcp2;
    523 		fi.fin_fr = &ftppxyfr;
    524 		fi.fin_out = nat->nat_dir;
    525 		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
    526 		swip = ip->ip_src;
    527 		swip2 = ip->ip_dst;
    528 		fi.fin_fi.fi_saddr = nat->nat_osrcaddr;
    529 		ip->ip_src = nat->nat_osrcip;
    530 		fi.fin_fi.fi_daddr = nat->nat_odstaddr;
    531 		ip->ip_dst = nat->nat_odstip;
    532 
    533 		flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
    534 		if (nat->nat_dir == NAT_INBOUND)
    535 			flags |= NAT_NOTRULEPORT;
    536 		MUTEX_ENTER(&softn->ipf_nat_new);
    537 		nat2 = ipf_nat_add(&fi, nat->nat_ptr, &ftp->ftp_pendnat,
    538 			       flags, nat->nat_dir);
    539 		MUTEX_EXIT(&softn->ipf_nat_new);
    540 
    541 		if (nat2 != NULL) {
    542 			(void) ipf_nat_proto(&fi, nat2, IPN_TCP);
    543 			MUTEX_ENTER(&nat2->nat_lock);
    544 			ipf_nat_update(&fi, nat2);
    545 			MUTEX_EXIT(&nat2->nat_lock);
    546 			fi.fin_ifp = NULL;
    547 			if (ipf_state_add(softc, &fi,
    548 					  (ipstate_t **)&ftp->ftp_pendstate,
    549 					  SI_W_DPORT) != 0) {
    550 				ipf_nat_setpending(softc, nat2);
    551 			}
    552 		}
    553 		ip->ip_len = slen;
    554 		ip->ip_src = swip;
    555 		ip->ip_dst = swip2;
    556 	}
    557 	return APR_INC(inc);
    558 }
    559 
    560 
    561 int
    562 ipf_p_ftp_client(softf, fin, ip, nat, ftp, dlen)
    563 	ipf_ftp_softc_t *softf;
    564 	fr_info_t *fin;
    565 	nat_t *nat;
    566 	ftpinfo_t *ftp;
    567 	ip_t *ip;
    568 	int dlen;
    569 {
    570 	char *rptr, *wptr, cmd[6], c;
    571 	ftpside_t *f;
    572 	int inc, i;
    573 
    574 	inc = 0;
    575 	f = &ftp->ftp_side[0];
    576 	rptr = f->ftps_rptr;
    577 	wptr = f->ftps_wptr;
    578 
    579 	for (i = 0; (i < 5) && (i < dlen); i++) {
    580 		c = rptr[i];
    581 		if (ISALPHA(c)) {
    582 			cmd[i] = TOUPPER(c);
    583 		} else {
    584 			cmd[i] = c;
    585 		}
    586 	}
    587 	cmd[i] = '\0';
    588 
    589 	ftp->ftp_incok = 0;
    590 	if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
    591 		if (ftp->ftp_passok == FTPXY_ADOK_1 ||
    592 		    ftp->ftp_passok == FTPXY_AUOK_1) {
    593 			ftp->ftp_passok = FTPXY_USER_2;
    594 			ftp->ftp_incok = 1;
    595 		} else {
    596 			ftp->ftp_passok = FTPXY_USER_1;
    597 			ftp->ftp_incok = 1;
    598 		}
    599 	} else if (!strncmp(cmd, "AUTH ", 5)) {
    600 		ftp->ftp_passok = FTPXY_AUTH_1;
    601 		ftp->ftp_incok = 1;
    602 	} else if (!strncmp(cmd, "PASS ", 5)) {
    603 		if (ftp->ftp_passok == FTPXY_USOK_1) {
    604 			ftp->ftp_passok = FTPXY_PASS_1;
    605 			ftp->ftp_incok = 1;
    606 		} else if (ftp->ftp_passok == FTPXY_USOK_2) {
    607 			ftp->ftp_passok = FTPXY_PASS_2;
    608 			ftp->ftp_incok = 1;
    609 		}
    610 	} else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
    611 		   !strncmp(cmd, "ADAT ", 5)) {
    612 		ftp->ftp_passok = FTPXY_ADAT_1;
    613 		ftp->ftp_incok = 1;
    614 	} else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
    615 		    ftp->ftp_passok == FTPXY_PAOK_2) &&
    616 		 !strncmp(cmd, "ACCT ", 5)) {
    617 		ftp->ftp_passok = FTPXY_ACCT_1;
    618 		ftp->ftp_incok = 1;
    619 	} else if ((ftp->ftp_passok == FTPXY_GO) &&
    620 		   !softf->ipf_p_ftp_pasvonly &&
    621 		 !strncmp(cmd, "PORT ", 5)) {
    622 		inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
    623 	} else if ((ftp->ftp_passok == FTPXY_GO) &&
    624 		   !softf->ipf_p_ftp_pasvonly &&
    625 		 !strncmp(cmd, "EPRT ", 5)) {
    626 		inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen);
    627 	} else if (softf->ipf_p_ftp_insecure &&
    628 		   !softf->ipf_p_ftp_pasvonly &&
    629 		   !strncmp(cmd, "PORT ", 5)) {
    630 		inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
    631 	}
    632 	if (softf->ipf_p_ftp_debug > 3)
    633 		printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n",
    634 		       cmd, ftp->ftp_passok, ftp->ftp_incok, inc);
    635 
    636 	while ((*rptr++ != '\n') && (rptr < wptr))
    637 		;
    638 	f->ftps_rptr = rptr;
    639 	return inc;
    640 }
    641 
    642 
    643 int
    644 ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen)
    645 	ipf_ftp_softc_t *softf;
    646 	fr_info_t *fin;
    647 	ip_t *ip;
    648 	nat_t *nat;
    649 	ftpinfo_t *ftp;
    650 	int dlen;
    651 {
    652 	u_int a1, a2, a3, a4, data_ip;
    653 	char newbuf[IPF_FTPBUFSZ];
    654 	const char *brackets[2];
    655 	u_short a5, a6;
    656 	ftpside_t *f;
    657 	char *s;
    658 
    659 	if ((softf->ipf_p_ftp_forcepasv != 0) &&
    660 	    (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) {
    661 		if (softf->ipf_p_ftp_debug > 0)
    662 			printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n",
    663 			       ftp->ftp_side[0].ftps_cmd);
    664 		return 0;
    665 	}
    666 
    667 	f = &ftp->ftp_side[1];
    668 
    669 #define	PASV_REPLEN	24
    670 	/*
    671 	 * Check for PASV reply message.
    672 	 */
    673 	if (dlen < IPF_MIN227LEN) {
    674 		if (softf->ipf_p_ftp_debug > 1)
    675 			printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
    676 			       dlen);
    677 		return 0;
    678 	} else if (strncmp(f->ftps_rptr,
    679 			   "227 Entering Passive Mod", PASV_REPLEN)) {
    680 		if (softf->ipf_p_ftp_debug > 0)
    681 			printf("ipf_p_ftp_pasv:%d reply wrong\n", 227);
    682 		return 0;
    683 	}
    684 
    685 	brackets[0] = "";
    686 	brackets[1] = "";
    687 	/*
    688 	 * Skip the PASV reply + space
    689 	 */
    690 	s = f->ftps_rptr + PASV_REPLEN;
    691 	while (*s && !ISDIGIT(*s)) {
    692 		if (*s == '(') {
    693 			brackets[0] = "(";
    694 			brackets[1] = ")";
    695 		}
    696 		s++;
    697 	}
    698 
    699 	/*
    700 	 * Pick out the address components, two at a time.
    701 	 */
    702 	a1 = ipf_p_ftp_atoi(&s);
    703 	if (s == NULL) {
    704 		if (softf->ipf_p_ftp_debug > 1)
    705 			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1);
    706 		return 0;
    707 	}
    708 	a2 = ipf_p_ftp_atoi(&s);
    709 	if (s == NULL) {
    710 		if (softf->ipf_p_ftp_debug > 1)
    711 			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2);
    712 		return 0;
    713 	}
    714 
    715 	/*
    716 	 * check that IP address in the PASV reply is the same as the
    717 	 * sender of the command - prevents using PASV for port scanning.
    718 	 */
    719 	a1 <<= 16;
    720 	a1 |= a2;
    721 
    722 	if (((nat->nat_dir == NAT_INBOUND) &&
    723 	     (a1 != ntohl(nat->nat_osrcaddr))) ||
    724 	    ((nat->nat_dir == NAT_OUTBOUND) &&
    725 	     (a1 != ntohl(nat->nat_odstaddr)))) {
    726 		if (softf->ipf_p_ftp_debug > 0)
    727 			printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1");
    728 		return 0;
    729 	}
    730 
    731 	a5 = ipf_p_ftp_atoi(&s);
    732 	if (s == NULL) {
    733 		if (softf->ipf_p_ftp_debug > 1)
    734 			printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3);
    735 		return 0;
    736 	}
    737 
    738 	if (*s == ')')
    739 		s++;
    740 	if (*s == '.')
    741 		s++;
    742 	if (*s == '\n')
    743 		s--;
    744 	/*
    745 	 * check for CR-LF at the end.
    746 	 */
    747 	if ((*s == '\r') && (*(s + 1) == '\n')) {
    748 		s += 2;
    749 	} else {
    750 		if (softf->ipf_p_ftp_debug > 1)
    751 			printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n");
    752 		return 0;
    753 	}
    754 
    755 	a6 = a5 & 0xff;
    756 	a5 >>= 8;
    757 	/*
    758 	 * Calculate new address parts for 227 reply
    759 	 */
    760 	if (nat->nat_dir == NAT_INBOUND) {
    761 		data_ip = nat->nat_ndstaddr;
    762 		a1 = ntohl(data_ip);
    763 	} else
    764 		data_ip = htonl(a1);
    765 
    766 	a2 = (a1 >> 16) & 0xff;
    767 	a3 = (a1 >> 8) & 0xff;
    768 	a4 = a1 & 0xff;
    769 	a1 >>= 24;
    770 
    771 #if defined(SNPRINTF) && defined(_KERNEL)
    772 	SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
    773 		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
    774 		a5, a6, brackets[1]);
    775 #else
    776 	(void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
    777 		"227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
    778 		a5, a6, brackets[1]);
    779 #endif
    780 	return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6),
    781 				  newbuf, s, data_ip);
    782 }
    783 
    784 int
    785 ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, port, newmsg, s, data_ip)
    786 	ipf_ftp_softc_t *softf;
    787 	fr_info_t *fin;
    788 	ip_t *ip;
    789 	nat_t *nat;
    790 	ftpinfo_t *ftp;
    791 	u_int port;
    792 	char *newmsg;
    793 	char *s;
    794 	u_int data_ip;
    795 {
    796 	int inc, off, nflags, sflags;
    797 	tcphdr_t *tcp, tcph, *tcp2;
    798 	struct in_addr swip, swip2;
    799 	struct in_addr data_addr;
    800 	ipf_main_softc_t *softc;
    801 	ipf_nat_softc_t *softn;
    802 	size_t nlen, olen;
    803 	fr_info_t fi;
    804 	ftpside_t *f;
    805 	nat_t *nat2;
    806 	mb_t *m;
    807 
    808 	softc = fin->fin_main_soft;
    809 	softn = softc->ipf_nat_soft;
    810 
    811 	if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL))
    812 		ipf_p_ftp_setpending(softc, ftp);
    813 
    814 	m = fin->fin_m;
    815 	tcp = (tcphdr_t *)fin->fin_dp;
    816 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
    817 
    818 	data_addr.s_addr = data_ip;
    819 	tcp2 = &tcph;
    820 	inc = 0;
    821 
    822 	f = &ftp->ftp_side[1];
    823 	olen = s - f->ftps_rptr;
    824 	nlen = strlen(newmsg);
    825 	inc = nlen - olen;
    826 	if ((inc + fin->fin_plen) > 65535) {
    827 		if (softf->ipf_p_ftp_debug > 0)
    828 			printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
    829 			       inc);
    830 		return 0;
    831 	}
    832 
    833 #if !defined(_KERNEL)
    834 	bcopy(newmsg, MTOD(m, char *) + off, nlen);
    835 #else
    836 	/*
    837 	 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
    838 	 * mean remove -len bytes from the end of the packet.
    839 	 * The mbuf chain will be extended if necessary by m_copyback().
    840 	 */
    841 	if (inc < 0)
    842 		M_ADJ(m, inc);
    843 #endif /* !defined(_KERNEL) */
    844 	COPYBACK(m, off, nlen, newmsg);
    845 
    846 	if (inc != 0) {
    847 		fin->fin_plen += inc;
    848 		ip->ip_len = htons(fin->fin_plen);
    849 		fin->fin_dlen += inc;
    850 	}
    851 
    852 	/*
    853 	 * Add skeleton NAT entry for connection which will come back the
    854 	 * other way.
    855 	 */
    856 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
    857 	fi.fin_flx |= FI_IGNORE;
    858 	fi.fin_data[0] = 0;
    859 	fi.fin_data[1] = port;
    860 	nflags = IPN_TCP|SI_W_SPORT;
    861 	if (softf->ipf_p_ftp_pasvrdr && f->ftps_ifp)
    862 		nflags |= SI_W_DPORT;
    863 	if (nat->nat_dir == NAT_OUTBOUND)
    864 		nat2 = ipf_nat_outlookup(&fi, nflags|NAT_SEARCH,
    865 				     nat->nat_pr[1], nat->nat_osrcip,
    866 				     nat->nat_odstip);
    867 	else
    868 		nat2 = ipf_nat_inlookup(&fi, nflags|NAT_SEARCH,
    869 				    nat->nat_pr[0], nat->nat_odstip,
    870 				    nat->nat_osrcip);
    871 	if (nat2 == NULL) {
    872 		int slen;
    873 
    874 		slen = ip->ip_len;
    875 		ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp2));
    876 		bzero((char *)tcp2, sizeof(*tcp2));
    877 		tcp2->th_win = htons(8192);
    878 		tcp2->th_sport = 0;		/* XXX - fake it for nat_new */
    879 		TCP_OFF_A(tcp2, 5);
    880 		tcp2->th_flags = TH_SYN;
    881 		fi.fin_data[1] = port;
    882 		fi.fin_dlen = sizeof(*tcp2);
    883 		tcp2->th_dport = htons(port);
    884 		fi.fin_data[0] = 0;
    885 		fi.fin_dp = (char *)tcp2;
    886 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
    887 		fi.fin_fr = &ftppxyfr;
    888 		fi.fin_out = nat->nat_dir;
    889 		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
    890 		swip = ip->ip_src;
    891 		swip2 = ip->ip_dst;
    892 		if (nat->nat_dir == NAT_OUTBOUND) {
    893 			fi.fin_fi.fi_daddr = data_addr.s_addr;
    894 			fi.fin_fi.fi_saddr = nat->nat_nsrcaddr;
    895 			ip->ip_dst = data_addr;
    896 			ip->ip_src = nat->nat_nsrcip;
    897 		} else if (nat->nat_dir == NAT_INBOUND) {
    898 			fi.fin_fi.fi_saddr = nat->nat_osrcaddr;
    899 			fi.fin_fi.fi_daddr = nat->nat_ndstaddr;
    900 			ip->ip_src = nat->nat_osrcip;
    901 			ip->ip_dst = nat->nat_odstip;
    902 		}
    903 
    904 		sflags = nflags;
    905 		nflags |= NAT_SLAVE;
    906 		if (nat->nat_dir == NAT_INBOUND)
    907 			nflags |= NAT_NOTRULEPORT;
    908 		MUTEX_ENTER(&softn->ipf_nat_new);
    909 		nat2 = ipf_nat_add(&fi, nat->nat_ptr, &ftp->ftp_pendnat,
    910 				   nflags, nat->nat_dir);
    911 		MUTEX_EXIT(&softn->ipf_nat_new);
    912 
    913 		if (nat2 != NULL) {
    914 			(void) ipf_nat_proto(&fi, nat2, IPN_TCP);
    915 			MUTEX_ENTER(&nat2->nat_lock);
    916 			ipf_nat_update(&fi, nat2);
    917 			MUTEX_EXIT(&nat2->nat_lock);
    918 			fi.fin_ifp = NULL;
    919 			if (nat->nat_dir == NAT_INBOUND) {
    920 				fi.fin_fi.fi_daddr = nat->nat_ndstaddr;
    921 				ip->ip_dst = nat->nat_ndstip;
    922 			}
    923 			if (ipf_state_add(softc, &fi,
    924 					  (ipstate_t **)&ftp->ftp_pendstate,
    925 					  sflags) != 0) {
    926 				ipf_nat_setpending(softc, nat2);
    927 			}
    928 		}
    929 
    930 		ip->ip_len = slen;
    931 		ip->ip_src = swip;
    932 		ip->ip_dst = swip2;
    933 	}
    934 	return inc;
    935 }
    936 
    937 
    938 int
    939 ipf_p_ftp_server(softf, fin, ip, nat, ftp, dlen)
    940 	ipf_ftp_softc_t *softf;
    941 	fr_info_t *fin;
    942 	ip_t *ip;
    943 	nat_t *nat;
    944 	ftpinfo_t *ftp;
    945 	int dlen;
    946 {
    947 	char *rptr, *wptr;
    948 	ftpside_t *f;
    949 	int inc;
    950 
    951 	inc = 0;
    952 	f = &ftp->ftp_side[1];
    953 	rptr = f->ftps_rptr;
    954 	wptr = f->ftps_wptr;
    955 
    956 	if (*rptr == ' ')
    957 		goto server_cmd_ok;
    958 	if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
    959 		return 0;
    960 	if (ftp->ftp_passok == FTPXY_GO) {
    961 		if (!strncmp(rptr, "227 ", 4))
    962 			inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
    963 		else if (!strncmp(rptr, "229 ", 4))
    964 			inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
    965 		else if (strncmp(rptr, "200", 3)) {
    966 			/*
    967 			 * 200 is returned for a successful command.
    968 			 */
    969 			;
    970 		}
    971 	} else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
    972 		inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
    973 	} else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
    974 		inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
    975 	} else if (*rptr == '5' || *rptr == '4')
    976 		ftp->ftp_passok = FTPXY_INIT;
    977 	else if (ftp->ftp_incok) {
    978 		if (*rptr == '3') {
    979 			if (ftp->ftp_passok == FTPXY_ACCT_1)
    980 				ftp->ftp_passok = FTPXY_GO;
    981 			else
    982 				ftp->ftp_passok++;
    983 		} else if (*rptr == '2') {
    984 			switch (ftp->ftp_passok)
    985 			{
    986 			case FTPXY_USER_1 :
    987 			case FTPXY_USER_2 :
    988 			case FTPXY_PASS_1 :
    989 			case FTPXY_PASS_2 :
    990 			case FTPXY_ACCT_1 :
    991 				ftp->ftp_passok = FTPXY_GO;
    992 				break;
    993 			default :
    994 				ftp->ftp_passok += 3;
    995 				break;
    996 			}
    997 		}
    998 	}
    999 server_cmd_ok:
   1000 	ftp->ftp_incok = 0;
   1001 
   1002 	while ((*rptr++ != '\n') && (rptr < wptr))
   1003 		;
   1004 	f->ftps_rptr = rptr;
   1005 	return inc;
   1006 }
   1007 
   1008 
   1009 /*
   1010  * Look to see if the buffer starts with something which we recognise as
   1011  * being the correct syntax for the FTP protocol.
   1012  */
   1013 int
   1014 ipf_p_ftp_client_valid(softf, ftps, buf, len)
   1015 	ipf_ftp_softc_t *softf;
   1016 	ftpside_t *ftps;
   1017 	char *buf;
   1018 	size_t len;
   1019 {
   1020 	register char *s, c, pc;
   1021 	register size_t i = len;
   1022 	char cmd[5];
   1023 
   1024 	s = buf;
   1025 
   1026 	if (ftps->ftps_junk == FTPXY_JUNK_BAD)
   1027 		return FTPXY_JUNK_BAD;
   1028 
   1029 	if (i < 5) {
   1030 		if (softf->ipf_p_ftp_debug > 3)
   1031 			printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i);
   1032 		return 2;
   1033 	}
   1034 
   1035 	i--;
   1036 	c = *s++;
   1037 
   1038 	if (ISALPHA(c)) {
   1039 		cmd[0] = TOUPPER(c);
   1040 		c = *s++;
   1041 		i--;
   1042 		if (ISALPHA(c)) {
   1043 			cmd[1] = TOUPPER(c);
   1044 			c = *s++;
   1045 			i--;
   1046 			if (ISALPHA(c)) {
   1047 				cmd[2] = TOUPPER(c);
   1048 				c = *s++;
   1049 				i--;
   1050 				if (ISALPHA(c)) {
   1051 					cmd[3] = TOUPPER(c);
   1052 					c = *s++;
   1053 					i--;
   1054 					if ((c != ' ') && (c != '\r'))
   1055 						goto bad_client_command;
   1056 				} else if ((c != ' ') && (c != '\r'))
   1057 					goto bad_client_command;
   1058 			} else
   1059 				goto bad_client_command;
   1060 		} else
   1061 			goto bad_client_command;
   1062 	} else {
   1063 bad_client_command:
   1064 		if (softf->ipf_p_ftp_debug > 3)
   1065 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
   1066 			       "ipf_p_ftp_client_valid",
   1067 			       ftps->ftps_junk, (int)len, (int)i, c,
   1068 			       (int)len, (int)len, buf);
   1069 		return FTPXY_JUNK_BAD;
   1070 	}
   1071 
   1072 	for (; i; i--) {
   1073 		pc = c;
   1074 		c = *s++;
   1075 		if ((pc == '\r') && (c == '\n')) {
   1076 			cmd[4] = '\0';
   1077 			if (!strcmp(cmd, "PASV"))
   1078 				ftps->ftps_cmd = FTPXY_C_PASV;
   1079 			else
   1080 				ftps->ftps_cmd = 0;
   1081 			return 0;
   1082 		}
   1083 	}
   1084 #if !defined(_KERNEL)
   1085 	printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n",
   1086 	       (int)len, (int)len, buf);
   1087 #endif
   1088 	return FTPXY_JUNK_EOL;
   1089 }
   1090 
   1091 
   1092 int
   1093 ipf_p_ftp_server_valid(softf, ftps, buf, len)
   1094 	ipf_ftp_softc_t *softf;
   1095 	ftpside_t *ftps;
   1096 	char *buf;
   1097 	size_t len;
   1098 {
   1099 	register char *s, c, pc;
   1100 	register size_t i = len;
   1101 	int cmd;
   1102 
   1103 	s = buf;
   1104 	cmd = 0;
   1105 
   1106 	if (ftps->ftps_junk == FTPXY_JUNK_BAD)
   1107 		return FTPXY_JUNK_BAD;
   1108 
   1109 	if (i < 5) {
   1110 		if (softf->ipf_p_ftp_debug > 3)
   1111 			printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i);
   1112 		return 2;
   1113 	}
   1114 
   1115 	c = *s++;
   1116 	i--;
   1117 	if (c == ' ') {
   1118 		cmd = -1;
   1119 		goto search_eol;
   1120 	}
   1121 
   1122 	if (ISDIGIT(c)) {
   1123 		cmd = (c - '0') * 100;
   1124 		c = *s++;
   1125 		i--;
   1126 		if (ISDIGIT(c)) {
   1127 			cmd += (c - '0') * 10;
   1128 			c = *s++;
   1129 			i--;
   1130 			if (ISDIGIT(c)) {
   1131 				cmd += (c - '0');
   1132 				c = *s++;
   1133 				i--;
   1134 				if ((c != '-') && (c != ' '))
   1135 					goto bad_server_command;
   1136 				if (c == '-')
   1137 					return FTPXY_JUNK_CONT;
   1138 			} else
   1139 				goto bad_server_command;
   1140 		} else
   1141 			goto bad_server_command;
   1142 	} else {
   1143 bad_server_command:
   1144 		if (softf->ipf_p_ftp_debug > 3)
   1145 			printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
   1146 			       "ipf_p_ftp_server_valid",
   1147 			       ftps->ftps_junk, (int)len, (int)i,
   1148 			       c, (int)len, (int)len, buf);
   1149 		if (ftps->ftps_junk == FTPXY_JUNK_CONT)
   1150 			return FTPXY_JUNK_CONT;
   1151 		return FTPXY_JUNK_BAD;
   1152 	}
   1153 search_eol:
   1154 	for (; i; i--) {
   1155 		pc = c;
   1156 		c = *s++;
   1157 		if ((pc == '\r') && (c == '\n')) {
   1158 			if (cmd == -1) {
   1159 				if (ftps->ftps_junk == FTPXY_JUNK_CONT)
   1160 					return FTPXY_JUNK_CONT;
   1161 			} else {
   1162 				ftps->ftps_cmd = cmd;
   1163 			}
   1164 			return FTPXY_JUNK_OK;
   1165 		}
   1166 	}
   1167 	if (softf->ipf_p_ftp_debug > 3)
   1168 		printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n",
   1169 		       (int)len, (int)len, buf);
   1170 	return FTPXY_JUNK_EOL;
   1171 }
   1172 
   1173 
   1174 int
   1175 ipf_p_ftp_valid(softf, ftp, side, buf, len)
   1176 	ipf_ftp_softc_t *softf;
   1177 	ftpinfo_t *ftp;
   1178 	int side;
   1179 	char *buf;
   1180 	size_t len;
   1181 {
   1182 	ftpside_t *ftps;
   1183 	int ret;
   1184 
   1185 	ftps = &ftp->ftp_side[side];
   1186 
   1187 	if (side == 0)
   1188 		ret = ipf_p_ftp_client_valid(softf, ftps, buf, len);
   1189 	else
   1190 		ret = ipf_p_ftp_server_valid(softf, ftps, buf, len);
   1191 	return ret;
   1192 }
   1193 
   1194 
   1195 /*
   1196  * For map rules, the following applies:
   1197  * rv == 0 for outbound processing,
   1198  * rv == 1 for inbound processing.
   1199  * For rdr rules, the following applies:
   1200  * rv == 0 for inbound processing,
   1201  * rv == 1 for outbound processing.
   1202  */
   1203 int
   1204 ipf_p_ftp_process(softf, fin, nat, ftp, rv)
   1205 	ipf_ftp_softc_t *softf;
   1206 	fr_info_t *fin;
   1207 	nat_t *nat;
   1208 	ftpinfo_t *ftp;
   1209 	int rv;
   1210 {
   1211 	int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry;
   1212 	char *rptr, *wptr, *s;
   1213 	u_32_t thseq, thack;
   1214 	ap_session_t *aps;
   1215 	ftpside_t *f, *t;
   1216 	tcphdr_t *tcp;
   1217 	ip_t *ip;
   1218 	mb_t *m;
   1219 
   1220 	m = fin->fin_m;
   1221 	ip = fin->fin_ip;
   1222 	tcp = (tcphdr_t *)fin->fin_dp;
   1223 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
   1224 
   1225 	f = &ftp->ftp_side[rv];
   1226 	t = &ftp->ftp_side[1 - rv];
   1227 	thseq = ntohl(tcp->th_seq);
   1228 	thack = ntohl(tcp->th_ack);
   1229 
   1230 #ifdef __sgi
   1231 	mlen = fin->fin_plen - off;
   1232 #else
   1233 	mlen = MSGDSIZE(m) - off;
   1234 #endif
   1235 	if (softf->ipf_p_ftp_debug > 4)
   1236 		printf("ipf_p_ftp_process: mlen %d\n", mlen);
   1237 
   1238 	if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) {
   1239 		f->ftps_seq[0] = thseq + 1;
   1240 		t->ftps_seq[0] = thack;
   1241 		return 0;
   1242 	} else if (mlen < 0) {
   1243 		return 0;
   1244 	}
   1245 
   1246 	aps = nat->nat_aps;
   1247 
   1248 	sel = aps->aps_sel[1 - rv];
   1249 	sel2 = aps->aps_sel[rv];
   1250 	if (rv == 0) {
   1251 		seqoff = aps->aps_seqoff[sel];
   1252 		if (aps->aps_seqmin[sel] > seqoff + thseq)
   1253 			seqoff = aps->aps_seqoff[!sel];
   1254 		ackoff = aps->aps_ackoff[sel2];
   1255 		if (aps->aps_ackmin[sel2] > ackoff + thack)
   1256 			ackoff = aps->aps_ackoff[!sel2];
   1257 	} else {
   1258 		seqoff = aps->aps_ackoff[sel];
   1259 		if (softf->ipf_p_ftp_debug > 2)
   1260 			printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
   1261 			       aps->aps_ackmin[sel]);
   1262 		if (aps->aps_ackmin[sel] > seqoff + thseq)
   1263 			seqoff = aps->aps_ackoff[!sel];
   1264 
   1265 		ackoff = aps->aps_seqoff[sel2];
   1266 		if (softf->ipf_p_ftp_debug > 2)
   1267 			printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
   1268 			       aps->aps_seqmin[sel2]);
   1269 		if (ackoff > 0) {
   1270 			if (aps->aps_seqmin[sel2] > ackoff + thack)
   1271 				ackoff = aps->aps_seqoff[!sel2];
   1272 		} else {
   1273 			if (aps->aps_seqmin[sel2] > thack)
   1274 				ackoff = aps->aps_seqoff[!sel2];
   1275 		}
   1276 	}
   1277 	if (softf->ipf_p_ftp_debug > 2) {
   1278 		printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
   1279 		       rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
   1280 		       thack, ackoff, mlen, fin->fin_plen, off);
   1281 		printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
   1282 		       aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
   1283 		       aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
   1284 		printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
   1285 		       aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
   1286 		       aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
   1287 	}
   1288 
   1289 	/*
   1290 	 * XXX - Ideally, this packet should get dropped because we now know
   1291 	 * that it is out of order (and there is no real danger in doing so
   1292 	 * apart from causing packets to go through here ordered).
   1293 	 */
   1294 	if (softf->ipf_p_ftp_debug > 2) {
   1295 		printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
   1296 		       rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
   1297 	}
   1298 
   1299 	ok = 0;
   1300 	if (t->ftps_seq[0] == 0) {
   1301 		t->ftps_seq[0] = thack;
   1302 		ok = 1;
   1303 	} else {
   1304 		if (ackoff == 0) {
   1305 			if (t->ftps_seq[0] == thack)
   1306 				ok = 1;
   1307 			else if (t->ftps_seq[1] == thack) {
   1308 				t->ftps_seq[0] = thack;
   1309 				ok = 1;
   1310 			}
   1311 		} else {
   1312 			if (t->ftps_seq[0] + ackoff == thack)
   1313 				ok = 1;
   1314 			else if (t->ftps_seq[0] == thack + ackoff)
   1315 				ok = 1;
   1316 			else if (t->ftps_seq[1] + ackoff == thack) {
   1317 				t->ftps_seq[0] = thack - ackoff;
   1318 				ok = 1;
   1319 			} else if (t->ftps_seq[1] == thack + ackoff) {
   1320 				t->ftps_seq[0] = thack - ackoff;
   1321 				ok = 1;
   1322 			}
   1323 		}
   1324 	}
   1325 
   1326 	if (softf->ipf_p_ftp_debug > 2) {
   1327 		if (!ok)
   1328 			printf("%s ok\n", "not");
   1329 	}
   1330 
   1331 	if (!mlen) {
   1332 		if (t->ftps_seq[0] + ackoff != thack) {
   1333 			if (softf->ipf_p_ftp_debug > 1) {
   1334 				printf("%s:seq[0](%x) + (%x) != (%x)\n",
   1335 				       "ipf_p_ftp_process", t->ftps_seq[0],
   1336 				       ackoff, thack);
   1337 			}
   1338 			return APR_ERR(1);
   1339 		}
   1340 
   1341 		if (softf->ipf_p_ftp_debug > 2) {
   1342 			printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n",
   1343 				f->ftps_seq[0], f->ftps_seq[1]);
   1344 		}
   1345 
   1346 		if (tcp->th_flags & TH_FIN) {
   1347 			if (thseq == f->ftps_seq[1]) {
   1348 				f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
   1349 				f->ftps_seq[1] = thseq + 1 - seqoff;
   1350 			} else {
   1351 				if (softf->ipf_p_ftp_debug > 1) {
   1352 					printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
   1353 					       thseq, seqoff, f->ftps_seq[0]);
   1354 				}
   1355 				return APR_ERR(1);
   1356 			}
   1357 		}
   1358 		f->ftps_len = 0;
   1359 		return 0;
   1360 	}
   1361 
   1362 	ok = 0;
   1363 	if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
   1364 		ok = 1;
   1365 	/*
   1366 	 * Retransmitted data packet.
   1367 	 */
   1368 	} else if ((thseq + mlen == f->ftps_seq[0]) ||
   1369 		   (thseq + mlen == f->ftps_seq[1])) {
   1370 		ok = 1;
   1371 	}
   1372 
   1373 	if (ok == 0) {
   1374 		inc = thseq - f->ftps_seq[0];
   1375 		if (softf->ipf_p_ftp_debug > 1) {
   1376 			printf("inc %d sel %d rv %d\n", inc, sel, rv);
   1377 			printf("th_seq %x ftps_seq %x/%x\n",
   1378 			       thseq, f->ftps_seq[0], f->ftps_seq[1]);
   1379 			printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
   1380 			       aps->aps_ackoff[sel]);
   1381 			printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
   1382 			       aps->aps_seqoff[sel]);
   1383 		}
   1384 
   1385 		return APR_ERR(1);
   1386 	}
   1387 
   1388 	inc = 0;
   1389 	rptr = f->ftps_rptr;
   1390 	wptr = f->ftps_wptr;
   1391 	f->ftps_seq[0] = thseq;
   1392 	f->ftps_seq[1] = f->ftps_seq[0] + mlen;
   1393 	f->ftps_len = mlen;
   1394 
   1395 	while (mlen > 0) {
   1396 		len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
   1397 		if (len == 0)
   1398 			break;
   1399 		COPYDATA(m, off, len, wptr);
   1400 		mlen -= len;
   1401 		off += len;
   1402 		wptr += len;
   1403 
   1404 whilemore:
   1405 		if (softf->ipf_p_ftp_debug > 3)
   1406 			printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
   1407 			       "ipf_p_ftp_process",
   1408 			       len, mlen, off, (u_long)wptr, f->ftps_junk,
   1409 			       len, len, rptr);
   1410 
   1411 		f->ftps_wptr = wptr;
   1412 		if (f->ftps_junk != FTPXY_JUNK_OK) {
   1413 			i = f->ftps_junk;
   1414 			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr,
   1415 						      wptr - rptr);
   1416 
   1417 			if (softf->ipf_p_ftp_debug > 5)
   1418 				printf("%s:junk %d -> %d\n",
   1419 				       "ipf_p_ftp_process", i, f->ftps_junk);
   1420 
   1421 			if (f->ftps_junk == FTPXY_JUNK_BAD) {
   1422 				if (wptr - rptr == sizeof(f->ftps_buf)) {
   1423 					if (softf->ipf_p_ftp_debug > 4)
   1424 						printf("%s:full buffer\n",
   1425 						       "ipf_p_ftp_process");
   1426 					f->ftps_rptr = f->ftps_buf;
   1427 					f->ftps_wptr = f->ftps_buf;
   1428 					rptr = f->ftps_rptr;
   1429 					wptr = f->ftps_wptr;
   1430 					continue;
   1431 				}
   1432 			}
   1433 		}
   1434 
   1435 		while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) {
   1436 			len = wptr - rptr;
   1437 			f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv,
   1438 						       rptr, len);
   1439 
   1440 			if (softf->ipf_p_ftp_debug > 3) {
   1441 				printf("%s=%d len %d rv %d ptr %lx/%lx ",
   1442 				       "ipf_p_ftp_valid",
   1443 				       f->ftps_junk, len, rv, (u_long)rptr,
   1444 				       (u_long)wptr);
   1445 				printf("buf [%*.*s]\n", len, len, rptr);
   1446 			}
   1447 
   1448 			if (f->ftps_junk == FTPXY_JUNK_OK) {
   1449 				f->ftps_cmds++;
   1450 				f->ftps_rptr = rptr;
   1451 				if (rv)
   1452 					inc += ipf_p_ftp_server(softf, fin,
   1453 								ip, nat,
   1454 								ftp, len);
   1455 				else
   1456 					inc += ipf_p_ftp_client(softf, fin,
   1457 								ip, nat,
   1458 								ftp, len);
   1459 				rptr = f->ftps_rptr;
   1460 				wptr = f->ftps_wptr;
   1461 			}
   1462 		}
   1463 
   1464 		/*
   1465 		 * Off to a bad start so lets just forget about using the
   1466 		 * ftp proxy for this connection.
   1467 		 */
   1468 		if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) {
   1469 			/* f->ftps_seq[1] += inc; */
   1470 
   1471 			if (softf->ipf_p_ftp_debug > 1)
   1472 				printf("%s:cmds == 0 junk == 1\n",
   1473 				       "ipf_p_ftp_process");
   1474 			return APR_ERR(2);
   1475 		}
   1476 
   1477 		retry = 0;
   1478 		if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) {
   1479 			for (s = rptr; s < wptr; s++) {
   1480 				if ((*s == '\r') && (s + 1 < wptr) &&
   1481 				    (*(s + 1) == '\n')) {
   1482 					rptr = s + 2;
   1483 					retry = 1;
   1484 					if (f->ftps_junk != FTPXY_JUNK_CONT)
   1485 						f->ftps_junk = FTPXY_JUNK_OK;
   1486 					break;
   1487 				}
   1488 			}
   1489 		}
   1490 
   1491 		if (rptr == wptr) {
   1492 			rptr = wptr = f->ftps_buf;
   1493 		} else {
   1494 			/*
   1495 			 * Compact the buffer back to the start.  The junk
   1496 			 * flag should already be set and because we're not
   1497 			 * throwing away any data, it is preserved from its
   1498 			 * current state.
   1499 			 */
   1500 			if (rptr > f->ftps_buf) {
   1501 				bcopy(rptr, f->ftps_buf, wptr - rptr);
   1502 				wptr -= rptr - f->ftps_buf;
   1503 				rptr = f->ftps_buf;
   1504 			}
   1505 		}
   1506 		f->ftps_rptr = rptr;
   1507 		f->ftps_wptr = wptr;
   1508 		if (retry)
   1509 			goto whilemore;
   1510 	}
   1511 
   1512 	/* f->ftps_seq[1] += inc; */
   1513 	if (tcp->th_flags & TH_FIN)
   1514 		f->ftps_seq[1]++;
   1515 	if (softf->ipf_p_ftp_debug > 3) {
   1516 #ifdef __sgi
   1517 		mlen = fin->fin_plen;
   1518 #else
   1519 		mlen = MSGDSIZE(m);
   1520 #endif
   1521 		mlen -= off;
   1522 		printf("ftps_seq[1] = %x inc %d len %d\n",
   1523 		       f->ftps_seq[1], inc, mlen);
   1524 	}
   1525 
   1526 	f->ftps_rptr = rptr;
   1527 	f->ftps_wptr = wptr;
   1528 	return APR_INC(inc);
   1529 }
   1530 
   1531 
   1532 int
   1533 ipf_p_ftp_out(arg, fin, aps, nat)
   1534 	void *arg;
   1535 	fr_info_t *fin;
   1536 	ap_session_t *aps;
   1537 	nat_t *nat;
   1538 {
   1539 	ipf_ftp_softc_t *softf = arg;
   1540 	ftpinfo_t *ftp;
   1541 	int rev;
   1542 
   1543 	ftp = aps->aps_data;
   1544 	if (ftp == NULL)
   1545 		return 0;
   1546 
   1547 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
   1548 	if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
   1549 		ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
   1550 
   1551 	return ipf_p_ftp_process(softf, fin, nat, ftp, rev);
   1552 }
   1553 
   1554 
   1555 int
   1556 ipf_p_ftp_in(arg, fin, aps, nat)
   1557 	void *arg;
   1558 	fr_info_t *fin;
   1559 	ap_session_t *aps;
   1560 	nat_t *nat;
   1561 {
   1562 	ipf_ftp_softc_t *softf = arg;
   1563 	ftpinfo_t *ftp;
   1564 	int rev;
   1565 
   1566 	ftp = aps->aps_data;
   1567 	if (ftp == NULL)
   1568 		return 0;
   1569 
   1570 	rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
   1571 	if (ftp->ftp_side[rev].ftps_ifp == NULL)
   1572 		ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
   1573 
   1574 	return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev);
   1575 }
   1576 
   1577 
   1578 /*
   1579  * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in
   1580  * pairs separated by commas (which are expected to be in the range 0 - 255),
   1581  * returning a 16 bit number combining either side of the , as the MSB and
   1582  * LSB.
   1583  */
   1584 u_short
   1585 ipf_p_ftp_atoi(ptr)
   1586 	char **ptr;
   1587 {
   1588 	register char *s = *ptr, c;
   1589 	register u_char i = 0, j = 0;
   1590 
   1591 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
   1592 		i *= 10;
   1593 		i += c - '0';
   1594 	}
   1595 	if (c != ',') {
   1596 		*ptr = NULL;
   1597 		return 0;
   1598 	}
   1599 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
   1600 		j *= 10;
   1601 		j += c - '0';
   1602 	}
   1603 	*ptr = s;
   1604 	i &= 0xff;
   1605 	j &= 0xff;
   1606 	return (i << 8) | j;
   1607 }
   1608 
   1609 
   1610 int
   1611 ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen)
   1612 	ipf_ftp_softc_t *softf;
   1613 	fr_info_t *fin;
   1614 	ip_t *ip;
   1615 	nat_t *nat;
   1616 	ftpinfo_t *ftp;
   1617 	int dlen;
   1618 {
   1619 	ftpside_t *f;
   1620 
   1621 	/*
   1622 	 * Check for client sending out EPRT message.
   1623 	 */
   1624 	if (dlen < IPF_MINEPRTLEN) {
   1625 		if (softf->ipf_p_ftp_debug > 1)
   1626 			printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n",
   1627 				dlen);
   1628 		return 0;
   1629 	}
   1630 
   1631 	/*
   1632 	 * Parse the EPRT command.  Format is:
   1633 	 * "EPRT |1|1.2.3.4|2000|" for IPv4 and
   1634 	 * "EPRT |2|ef00::1:2|2000|" for IPv6 (not supported, yet.)
   1635 	 */
   1636 	f = &ftp->ftp_side[0];
   1637 	if (f->ftps_rptr[5] == f->ftps_rptr[7]) {
   1638 		if (f->ftps_rptr[6] == '1')
   1639 			return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen);
   1640 	}
   1641 	return 0;
   1642 }
   1643 
   1644 
   1645 int
   1646 ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen)
   1647 	ipf_ftp_softc_t *softf;
   1648 	fr_info_t *fin;
   1649 	ip_t *ip;
   1650 	nat_t *nat;
   1651 	ftpinfo_t *ftp;
   1652 	int dlen;
   1653 {
   1654 	int a1, a2, a3, a4, port, olen, nlen, inc, off;
   1655 	char newbuf[IPF_FTPBUFSZ];
   1656 	char *s, c, delim;
   1657 	u_32_t addr, i;
   1658 	tcphdr_t *tcp;
   1659 	ftpside_t *f;
   1660 	mb_t *m;
   1661 
   1662 	m = fin->fin_m;
   1663 	tcp = (tcphdr_t *)fin->fin_dp;
   1664 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
   1665 	f = &ftp->ftp_side[0];
   1666 	delim = f->ftps_rptr[5];
   1667 	s = f->ftps_rptr + 8;
   1668 
   1669 	/*
   1670 	 * get the IP address.
   1671 	 */
   1672 	i = 0;
   1673 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
   1674 		i *= 10;
   1675 		i += c - '0';
   1676 	}
   1677 	if (i > 255)
   1678 		return 0;
   1679 	if (c != '.')
   1680 		return 0;
   1681 	addr = (i << 24);
   1682 
   1683 	i = 0;
   1684 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
   1685 		i *= 10;
   1686 		i += c - '0';
   1687 	}
   1688 	if (i > 255)
   1689 		return 0;
   1690 	if (c != '.')
   1691 		return 0;
   1692 	addr |= (addr << 16);
   1693 
   1694 	i = 0;
   1695 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
   1696 		i *= 10;
   1697 		i += c - '0';
   1698 	}
   1699 	if (i > 255)
   1700 		return 0;
   1701 	if (c != '.')
   1702 		return 0;
   1703 	addr |= (addr << 8);
   1704 
   1705 	i = 0;
   1706 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
   1707 		i *= 10;
   1708 		i += c - '0';
   1709 	}
   1710 	if (i > 255)
   1711 		return 0;
   1712 	if (c != delim)
   1713 		return 0;
   1714 	addr |= addr;
   1715 
   1716 	/*
   1717 	 * Get the port number
   1718 	 */
   1719 	i = 0;
   1720 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
   1721 		i *= 10;
   1722 		i += c - '0';
   1723 	}
   1724 	if (i > 65535)
   1725 		return 0;
   1726 	if (c != delim)
   1727 		return 0;
   1728 	port = i;
   1729 
   1730 	/*
   1731 	 * Check for CR-LF at the end of the command string.
   1732 	 */
   1733 	if ((*s != '\r') || (*(s + 1) != '\n')) {
   1734 		if (softf->ipf_p_ftp_debug > 1)
   1735 			printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf");
   1736 		return 0;
   1737 	}
   1738 
   1739 	/*
   1740 	 * Calculate new address parts for PORT command
   1741 	 */
   1742 	if (nat->nat_dir == NAT_INBOUND)
   1743 		a1 = ntohl(nat->nat_odstaddr);
   1744 	else
   1745 		a1 = ntohl(ip->ip_src.s_addr);
   1746 	a2 = (a1 >> 16) & 0xff;
   1747 	a3 = (a1 >> 8) & 0xff;
   1748 	a4 = a1 & 0xff;
   1749 	a1 >>= 24;
   1750 	olen = s - f->ftps_rptr;
   1751 	/* DO NOT change this to snprintf! */
   1752 	/*
   1753 	 * While we could force the use of | as a delimiter here, it makes
   1754 	 * sense to preserve whatever character is being used by the systems
   1755 	 * involved in the communication.
   1756 	 */
   1757 #if defined(SNPRINTF) && defined(_KERNEL)
   1758 	SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
   1759 		 "EPRT", delim, delim, a1, a2, a3, a4, port, delim);
   1760 #else
   1761 	(void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
   1762 		       "EPRT", delim, delim, a1, a2, a3, a4, delim, port,
   1763 			delim);
   1764 #endif
   1765 
   1766 	nlen = strlen(newbuf);
   1767 	inc = nlen - olen;
   1768 	if ((inc + fin->fin_plen) > 65535) {
   1769 		if (softf->ipf_p_ftp_debug > 0)
   1770 			printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n",
   1771 				inc);
   1772 		return 0;
   1773 	}
   1774 
   1775 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
   1776 #if !defined(_KERNEL)
   1777 	bcopy(newbuf, MTOD(m, char *) + off, nlen);
   1778 #else
   1779 	if (inc < 0)
   1780 		M_ADJ(m, inc);
   1781 #endif
   1782 	/* the mbuf chain will be extended if necessary by m_copyback() */
   1783 	COPYBACK(m, off, nlen, newbuf);
   1784 
   1785 	if (inc != 0) {
   1786 		fin->fin_plen += inc;
   1787 		ip->ip_len = htons(fin->fin_plen);
   1788 		fin->fin_dlen += inc;
   1789 	}
   1790 
   1791 	f->ftps_cmd = FTPXY_C_EPRT;
   1792 	return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
   1793 }
   1794 
   1795 
   1796 int
   1797 ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen)
   1798 	ipf_ftp_softc_t *softf;
   1799 	fr_info_t *fin;
   1800 	ip_t *ip;
   1801 	nat_t *nat;
   1802 	ftpinfo_t *ftp;
   1803 	int dlen;
   1804 {
   1805 	char newbuf[IPF_FTPBUFSZ];
   1806 	u_short ap = 0;
   1807 	ftpside_t *f;
   1808 	char *s;
   1809 
   1810 	if ((softf->ipf_p_ftp_forcepasv != 0) &&
   1811 	    (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) {
   1812 		if (softf->ipf_p_ftp_debug > 0)
   1813 			printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n",
   1814 			       ftp->ftp_side[0].ftps_cmd);
   1815 		return 0;
   1816 	}
   1817 	f = &ftp->ftp_side[1];
   1818 
   1819 #define EPSV_REPLEN	33
   1820 	/*
   1821 	 * Check for EPSV reply message.
   1822 	 */
   1823 	if (dlen < IPF_MIN229LEN)
   1824 		return (0);
   1825 	else if (strncmp(f->ftps_rptr,
   1826 			 "229 Entering Extended Passive Mode", EPSV_REPLEN))
   1827 		return (0);
   1828 
   1829 	/*
   1830 	 * Skip the EPSV command + space
   1831 	 */
   1832 	s = f->ftps_rptr + 33;
   1833 	while (*s && !ISDIGIT(*s))
   1834 		s++;
   1835 
   1836 	/*
   1837 	 * As per RFC 2428, there are no addres components in the EPSV
   1838 	 * response.  So we'll go straight to getting the port.
   1839 	 */
   1840 	while (*s && ISDIGIT(*s)) {
   1841 		ap *= 10;
   1842 		ap += *s++ - '0';
   1843 	}
   1844 
   1845 	if (!s)
   1846 		return 0;
   1847 
   1848 	if (*s == '|')
   1849 		s++;
   1850 	if (*s == ')')
   1851 		s++;
   1852 	if (*s == '\n')
   1853 		s--;
   1854 	/*
   1855 	 * check for CR-LF at the end.
   1856 	 */
   1857 	if ((*s == '\r') && (*(s + 1) == '\n')) {
   1858 		s += 2;
   1859 	} else
   1860 		return 0;
   1861 
   1862 #if defined(SNPRINTF) && defined(_KERNEL)
   1863 	SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
   1864 		 "229 Entering Extended Passive Mode", ap);
   1865 #else
   1866 	(void) sprintf(newbuf, "%s (|||%u|)\r\n",
   1867 		       "229 Entering Extended Passive Mode", ap);
   1868 #endif
   1869 
   1870 	return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap,
   1871 				   newbuf, s, ip->ip_src.s_addr);
   1872 }
   1873