xref: /freebsd/sys/netinet/libalias/alias_ftp.c (revision 0fc7bdc978366abb4351b0b76b50a5848cc5d982)
1f987e1bdSBrian Somers /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
4e83aaae3SBrian Somers  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
5f987e1bdSBrian Somers  * All rights reserved.
6f987e1bdSBrian Somers  *
7f987e1bdSBrian Somers  * Redistribution and use in source and binary forms, with or without
8f987e1bdSBrian Somers  * modification, are permitted provided that the following conditions
9f987e1bdSBrian Somers  * are met:
10f987e1bdSBrian Somers  * 1. Redistributions of source code must retain the above copyright
11f987e1bdSBrian Somers  *    notice, this list of conditions and the following disclaimer.
12f987e1bdSBrian Somers  * 2. Redistributions in binary form must reproduce the above copyright
13f987e1bdSBrian Somers  *    notice, this list of conditions and the following disclaimer in the
14f987e1bdSBrian Somers  *    documentation and/or other materials provided with the distribution.
15f987e1bdSBrian Somers  *
16f987e1bdSBrian Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17f987e1bdSBrian Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f987e1bdSBrian Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f987e1bdSBrian Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20f987e1bdSBrian Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f987e1bdSBrian Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f987e1bdSBrian Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f987e1bdSBrian Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f987e1bdSBrian Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f987e1bdSBrian Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f987e1bdSBrian Somers  * SUCH DAMAGE.
27f987e1bdSBrian Somers  */
28f987e1bdSBrian Somers 
29e2505aa6SMatthew Dillon #include <sys/cdefs.h>
303b160b8bSBrian Somers /*
313b160b8bSBrian Somers     Alias_ftp.c performs special processing for FTP sessions under
32b7666040SRuslan Ermilov     TCP.  Specifically, when a PORT/EPRT command from the client
33b7666040SRuslan Ermilov     side or 227/229 reply from the server is sent, it is intercepted
3476525129SRuslan Ermilov     and modified.  The address is changed to the gateway machine
3576525129SRuslan Ermilov     and an aliasing port is used.
363b160b8bSBrian Somers 
37b7666040SRuslan Ermilov     For this routine to work, the message must fit entirely into a
38b7666040SRuslan Ermilov     single TCP packet.  This is typically the case, but exceptions
393b160b8bSBrian Somers     can easily be envisioned under the actual specifications.
403b160b8bSBrian Somers 
413b160b8bSBrian Somers     Probably the most troubling aspect of the approach taken here is
42b7666040SRuslan Ermilov     that the new message will typically be a different length, and
433b160b8bSBrian Somers     this causes a certain amount of bookkeeping to keep track of the
443b160b8bSBrian Somers     changes of sequence and acknowledgment numbers, since the client
453b160b8bSBrian Somers     machine is totally unaware of the modification to the TCP stream.
463b160b8bSBrian Somers 
47b7666040SRuslan Ermilov     References: RFC 959, RFC 2428.
48b7666040SRuslan Ermilov 
493b160b8bSBrian Somers     Initial version:  August, 1996  (cjm)
503b160b8bSBrian Somers 
513b160b8bSBrian Somers     Version 1.6
523b160b8bSBrian Somers 	 Brian Somers and Martin Renters identified an IP checksum
533b160b8bSBrian Somers 	 error for modified IP packets.
543b160b8bSBrian Somers 
553b160b8bSBrian Somers     Version 1.7:  January 9, 1996 (cjm)
5667b333b7SRuslan Ermilov 	 Differential checksum computation for change
573b160b8bSBrian Somers 	 in IP packet length.
583b160b8bSBrian Somers 
593b160b8bSBrian Somers     Version 2.1:  May, 1997 (cjm)
603b160b8bSBrian Somers 	 Very minor changes to conform with
613b160b8bSBrian Somers 	 local/global/function naming conventions
6267b333b7SRuslan Ermilov 	 within the packet aliasing module.
638ddc51bcSEivind Eklund 
6476525129SRuslan Ermilov     Version 3.1:  May, 2000 (eds)
6576525129SRuslan Ermilov 	 Add support for passive mode, alias the 227 replies.
6676525129SRuslan Ermilov 
678ddc51bcSEivind Eklund     See HISTORY file for record of revisions.
683b160b8bSBrian Somers */
693b160b8bSBrian Somers 
703b160b8bSBrian Somers /* Includes */
71c649a2e0SGleb Smirnoff #ifdef _KERNEL
72c649a2e0SGleb Smirnoff #include <sys/param.h>
73c649a2e0SGleb Smirnoff #include <sys/ctype.h>
74be4f3cd0SPaolo Pisati #include <sys/systm.h>
75be4f3cd0SPaolo Pisati #include <sys/kernel.h>
76be4f3cd0SPaolo Pisati #include <sys/module.h>
77c649a2e0SGleb Smirnoff #else
78dd14bc5dSPaolo Pisati #include <ctype.h>
79be4f3cd0SPaolo Pisati #include <errno.h>
80c649a2e0SGleb Smirnoff #include <sys/types.h>
813b160b8bSBrian Somers #include <stdio.h>
82d8164209SAlexander Kabaev #include <string.h>
83c649a2e0SGleb Smirnoff #endif
84c649a2e0SGleb Smirnoff 
853b160b8bSBrian Somers #include <netinet/in_systm.h>
863b160b8bSBrian Somers #include <netinet/in.h>
873b160b8bSBrian Somers #include <netinet/ip.h>
883b160b8bSBrian Somers #include <netinet/tcp.h>
893b160b8bSBrian Somers 
90c649a2e0SGleb Smirnoff #ifdef _KERNEL
91c649a2e0SGleb Smirnoff #include <netinet/libalias/alias.h>
9275bc2620SGleb Smirnoff #include <netinet/libalias/alias_local.h>
93be4f3cd0SPaolo Pisati #include <netinet/libalias/alias_mod.h>
94c649a2e0SGleb Smirnoff #else
953b160b8bSBrian Somers #include "alias_local.h"
96be4f3cd0SPaolo Pisati #include "alias_mod.h"
97be4f3cd0SPaolo Pisati #endif
98be4f3cd0SPaolo Pisati 
99be4f3cd0SPaolo Pisati #define FTP_CONTROL_PORT_NUMBER 21
100be4f3cd0SPaolo Pisati 
101be4f3cd0SPaolo Pisati static void
102be4f3cd0SPaolo Pisati AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *,
103be4f3cd0SPaolo Pisati     int maxpacketsize);
10405116753SAndrey V. Elsukov static void
10505116753SAndrey V. Elsukov AliasHandleFtpIn(struct libalias *, struct ip *, struct alias_link *);
106be4f3cd0SPaolo Pisati 
107be4f3cd0SPaolo Pisati static int
fingerprint_out(struct libalias * la,struct alias_data * ah)10805116753SAndrey V. Elsukov fingerprint_out(struct libalias *la, struct alias_data *ah)
109be4f3cd0SPaolo Pisati {
110be4f3cd0SPaolo Pisati 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
111be4f3cd0SPaolo Pisati 	    ah->maxpktsize == 0)
112be4f3cd0SPaolo Pisati 		return (-1);
11305116753SAndrey V. Elsukov 	if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
11405116753SAndrey V. Elsukov 	    ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
115be4f3cd0SPaolo Pisati 		return (0);
116be4f3cd0SPaolo Pisati 	return (-1);
117be4f3cd0SPaolo Pisati }
118be4f3cd0SPaolo Pisati 
119be4f3cd0SPaolo Pisati static int
fingerprint_in(struct libalias * la,struct alias_data * ah)12005116753SAndrey V. Elsukov fingerprint_in(struct libalias *la, struct alias_data *ah)
12105116753SAndrey V. Elsukov {
12205116753SAndrey V. Elsukov 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
12305116753SAndrey V. Elsukov 		return (-1);
12405116753SAndrey V. Elsukov 	if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
12505116753SAndrey V. Elsukov 	    ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
12605116753SAndrey V. Elsukov 		return (0);
12705116753SAndrey V. Elsukov 	return (-1);
12805116753SAndrey V. Elsukov }
12905116753SAndrey V. Elsukov 
13005116753SAndrey V. Elsukov static int
protohandler_out(struct libalias * la,struct ip * pip,struct alias_data * ah)13105116753SAndrey V. Elsukov protohandler_out(struct libalias *la, struct ip *pip, struct alias_data *ah)
132be4f3cd0SPaolo Pisati {
133be4f3cd0SPaolo Pisati 	AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
134be4f3cd0SPaolo Pisati 	return (0);
135be4f3cd0SPaolo Pisati }
136be4f3cd0SPaolo Pisati 
13705116753SAndrey V. Elsukov static int
protohandler_in(struct libalias * la,struct ip * pip,struct alias_data * ah)13805116753SAndrey V. Elsukov protohandler_in(struct libalias *la, struct ip *pip, struct alias_data *ah)
13905116753SAndrey V. Elsukov {
14005116753SAndrey V. Elsukov 	AliasHandleFtpIn(la, pip, ah->lnk);
14105116753SAndrey V. Elsukov 	return (0);
14205116753SAndrey V. Elsukov }
14305116753SAndrey V. Elsukov 
144be4f3cd0SPaolo Pisati struct proto_handler handlers[] = {
145be4f3cd0SPaolo Pisati 	{
146be4f3cd0SPaolo Pisati 	  .pri = 80,
147be4f3cd0SPaolo Pisati 	  .dir = OUT,
148be4f3cd0SPaolo Pisati 	  .proto = TCP,
14905116753SAndrey V. Elsukov 	  .fingerprint = &fingerprint_out,
15005116753SAndrey V. Elsukov 	  .protohandler = &protohandler_out
15105116753SAndrey V. Elsukov 	},
15205116753SAndrey V. Elsukov 	{
15305116753SAndrey V. Elsukov 	  .pri = 80,
15405116753SAndrey V. Elsukov 	  .dir = IN,
15505116753SAndrey V. Elsukov 	  .proto = TCP,
15605116753SAndrey V. Elsukov 	  .fingerprint = &fingerprint_in,
15705116753SAndrey V. Elsukov 	  .protohandler = &protohandler_in
158be4f3cd0SPaolo Pisati 	},
159be4f3cd0SPaolo Pisati 	{ EOH }
160be4f3cd0SPaolo Pisati };
161be4f3cd0SPaolo Pisati 
162be4f3cd0SPaolo Pisati static int
mod_handler(module_t mod,int type,void * data)163be4f3cd0SPaolo Pisati mod_handler(module_t mod, int type, void *data)
164be4f3cd0SPaolo Pisati {
165be4f3cd0SPaolo Pisati 	int error;
166be4f3cd0SPaolo Pisati 
167be4f3cd0SPaolo Pisati 	switch (type) {
168be4f3cd0SPaolo Pisati 	case MOD_LOAD:
169be4f3cd0SPaolo Pisati 		error = 0;
170be4f3cd0SPaolo Pisati 		LibAliasAttachHandlers(handlers);
171be4f3cd0SPaolo Pisati 		break;
172be4f3cd0SPaolo Pisati 	case MOD_UNLOAD:
173be4f3cd0SPaolo Pisati 		error = 0;
174be4f3cd0SPaolo Pisati 		LibAliasDetachHandlers(handlers);
175be4f3cd0SPaolo Pisati 		break;
176be4f3cd0SPaolo Pisati 	default:
177be4f3cd0SPaolo Pisati 		error = EINVAL;
178be4f3cd0SPaolo Pisati 	}
179be4f3cd0SPaolo Pisati 	return (error);
180be4f3cd0SPaolo Pisati }
181be4f3cd0SPaolo Pisati 
182be4f3cd0SPaolo Pisati #ifdef _KERNEL
183be4f3cd0SPaolo Pisati static
184be4f3cd0SPaolo Pisati #endif
185be4f3cd0SPaolo Pisati moduledata_t alias_mod = {
186be4f3cd0SPaolo Pisati        "alias_ftp", mod_handler, NULL
187be4f3cd0SPaolo Pisati };
188be4f3cd0SPaolo Pisati 
189be4f3cd0SPaolo Pisati #ifdef _KERNEL
190be4f3cd0SPaolo Pisati DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
191be4f3cd0SPaolo Pisati MODULE_VERSION(alias_ftp, 1);
192be4f3cd0SPaolo Pisati MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1);
193c649a2e0SGleb Smirnoff #endif
1943b160b8bSBrian Somers 
19576525129SRuslan Ermilov #define FTP_CONTROL_PORT_NUMBER 21
196b7666040SRuslan Ermilov #define MAX_MESSAGE_SIZE	128
19776525129SRuslan Ermilov 
19879ec1c50SRuslan Ermilov /* FTP protocol flags. */
19979ec1c50SRuslan Ermilov #define WAIT_CRLF		0x01
20079ec1c50SRuslan Ermilov 
201b7666040SRuslan Ermilov enum ftp_message_type {
202b7666040SRuslan Ermilov 	FTP_PORT_COMMAND,
203b7666040SRuslan Ermilov 	FTP_EPRT_COMMAND,
204b7666040SRuslan Ermilov 	FTP_227_REPLY,
205b7666040SRuslan Ermilov 	FTP_229_REPLY,
206b7666040SRuslan Ermilov 	FTP_UNKNOWN_MESSAGE
207b7666040SRuslan Ermilov };
2083b160b8bSBrian Somers 
2095e289f9eSPoul-Henning Kamp static int	ParseFtpPortCommand(struct libalias *la, char *, int);
2105e289f9eSPoul-Henning Kamp static int	ParseFtpEprtCommand(struct libalias *la, char *, int);
2115e289f9eSPoul-Henning Kamp static int	ParseFtp227Reply(struct libalias *la, char *, int);
2125e289f9eSPoul-Henning Kamp static int	ParseFtp229Reply(struct libalias *la, char *, int);
2135e289f9eSPoul-Henning Kamp static void	NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int);
2143b160b8bSBrian Somers 
215be4f3cd0SPaolo Pisati static void
AliasHandleFtpOut(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize)2163b160b8bSBrian Somers AliasHandleFtpOut(
2175e289f9eSPoul-Henning Kamp     struct libalias *la,
2183b160b8bSBrian Somers     struct ip *pip,		/* IP packet to examine/patch */
219ed01a582SDag-Erling Smørgrav     struct alias_link *lnk,	/* The link to go through (aliased port) */
220f0f93429SDag-Erling Smørgrav     int maxpacketsize		/* The maximum size this packet can grow to
221f0f93429SDag-Erling Smørgrav 				   (including headers) */ )
2223b160b8bSBrian Somers {
22379ec1c50SRuslan Ermilov 	int hlen, tlen, dlen, pflags;
2243b160b8bSBrian Somers 	char *sptr;
2253b160b8bSBrian Somers 	struct tcphdr *tc;
226b7666040SRuslan Ermilov 	int ftp_message_type;
2273b160b8bSBrian Somers 
2283b160b8bSBrian Somers 	/* Calculate data length of TCP packet */
2299fa0fd26SDag-Erling Smørgrav 	tc = (struct tcphdr *)ip_next(pip);
2303b160b8bSBrian Somers 	hlen = (pip->ip_hl + tc->th_off) << 2;
2313b160b8bSBrian Somers 	tlen = ntohs(pip->ip_len);
2323b160b8bSBrian Somers 	dlen = tlen - hlen;
2333b160b8bSBrian Somers 
2343b160b8bSBrian Somers 	/* Place string pointer and beginning of data */
2353b160b8bSBrian Somers 	sptr = (char *)pip;
2363b160b8bSBrian Somers 	sptr += hlen;
2373b160b8bSBrian Somers 
238b7666040SRuslan Ermilov 	/*
239b7666040SRuslan Ermilov 	 * Check that data length is not too long and previous message was
240b7666040SRuslan Ermilov 	 * properly terminated with CRLF.
241b7666040SRuslan Ermilov 	 */
242ed01a582SDag-Erling Smørgrav 	pflags = GetProtocolFlags(lnk);
2433277d1c4SRuslan Ermilov 	if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) {
244b7666040SRuslan Ermilov 		ftp_message_type = FTP_UNKNOWN_MESSAGE;
245b7666040SRuslan Ermilov 
24676525129SRuslan Ermilov 		if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
247effc8e57SLutz Donnerhacke 			/* When aliasing a client, check for the PORT/EPRT command. */
2485e289f9eSPoul-Henning Kamp 			if (ParseFtpPortCommand(la, sptr, dlen))
249b7666040SRuslan Ermilov 				ftp_message_type = FTP_PORT_COMMAND;
2505e289f9eSPoul-Henning Kamp 			else if (ParseFtpEprtCommand(la, sptr, dlen))
251b7666040SRuslan Ermilov 				ftp_message_type = FTP_EPRT_COMMAND;
25276525129SRuslan Ermilov 		} else {
253effc8e57SLutz Donnerhacke 			/* When aliasing a server, check for the 227/229 reply. */
2545e289f9eSPoul-Henning Kamp 			if (ParseFtp227Reply(la, sptr, dlen))
255b7666040SRuslan Ermilov 				ftp_message_type = FTP_227_REPLY;
2565e289f9eSPoul-Henning Kamp 			else if (ParseFtp229Reply(la, sptr, dlen)) {
257b7666040SRuslan Ermilov 				ftp_message_type = FTP_229_REPLY;
2585e289f9eSPoul-Henning Kamp 				la->true_addr.s_addr = pip->ip_src.s_addr;
259c1dd00f7SRuslan Ermilov 			}
260b7666040SRuslan Ermilov 		}
261b7666040SRuslan Ermilov 
262b7666040SRuslan Ermilov 		if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
263ed01a582SDag-Erling Smørgrav 			NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type);
26476525129SRuslan Ermilov 	}
26576525129SRuslan Ermilov 
266effc8e57SLutz Donnerhacke 	/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
26776525129SRuslan Ermilov 	if (dlen) {			/* only if there's data */
26876525129SRuslan Ermilov 		sptr = (char *)pip;	/* start over at beginning */
269effc8e57SLutz Donnerhacke 		tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may have grown */
27079ec1c50SRuslan Ermilov 		if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
27179ec1c50SRuslan Ermilov 			pflags &= ~WAIT_CRLF;
27279ec1c50SRuslan Ermilov 		else
27379ec1c50SRuslan Ermilov 			pflags |= WAIT_CRLF;
274ed01a582SDag-Erling Smørgrav 		SetProtocolFlags(lnk, pflags);
27576525129SRuslan Ermilov 	}
27667b333b7SRuslan Ermilov }
27767b333b7SRuslan Ermilov 
27805116753SAndrey V. Elsukov static void
AliasHandleFtpIn(struct libalias * la,struct ip * pip,struct alias_link * lnk)27905116753SAndrey V. Elsukov AliasHandleFtpIn(struct libalias *la,
28005116753SAndrey V. Elsukov     struct ip *pip,		/* IP packet to examine/patch */
28105116753SAndrey V. Elsukov     struct alias_link *lnk)	/* The link to go through (aliased port) */
28205116753SAndrey V. Elsukov {
28305116753SAndrey V. Elsukov 	int hlen, tlen, dlen, pflags;
28405116753SAndrey V. Elsukov 	char *sptr;
28505116753SAndrey V. Elsukov 	struct tcphdr *tc;
28605116753SAndrey V. Elsukov 
28705116753SAndrey V. Elsukov 	/* Calculate data length of TCP packet */
28805116753SAndrey V. Elsukov 	tc = (struct tcphdr *)ip_next(pip);
28905116753SAndrey V. Elsukov 	hlen = (pip->ip_hl + tc->th_off) << 2;
29005116753SAndrey V. Elsukov 	tlen = ntohs(pip->ip_len);
29105116753SAndrey V. Elsukov 	dlen = tlen - hlen;
29205116753SAndrey V. Elsukov 
29305116753SAndrey V. Elsukov 	/* Place string pointer and beginning of data */
29405116753SAndrey V. Elsukov 	sptr = (char *)pip;
29505116753SAndrey V. Elsukov 	sptr += hlen;
29605116753SAndrey V. Elsukov 
29705116753SAndrey V. Elsukov 	/*
29805116753SAndrey V. Elsukov 	 * Check that data length is not too long and previous message was
29905116753SAndrey V. Elsukov 	 * properly terminated with CRLF.
30005116753SAndrey V. Elsukov 	 */
30105116753SAndrey V. Elsukov 	pflags = GetProtocolFlags(lnk);
30205116753SAndrey V. Elsukov 	if (dlen <= MAX_MESSAGE_SIZE && (pflags & WAIT_CRLF) == 0 &&
30305116753SAndrey V. Elsukov 	    ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER &&
30405116753SAndrey V. Elsukov 	    (ParseFtpPortCommand(la, sptr, dlen) != 0 ||
30505116753SAndrey V. Elsukov 	        ParseFtpEprtCommand(la, sptr, dlen) != 0)) {
30605116753SAndrey V. Elsukov 		/*
30705116753SAndrey V. Elsukov 		 * Alias active mode client requesting data from server
30805116753SAndrey V. Elsukov 		 * behind NAT.  We need to alias server->client connection
30905116753SAndrey V. Elsukov 		 * to external address client is connecting to.
31005116753SAndrey V. Elsukov 		 */
31105116753SAndrey V. Elsukov 		AddLink(la, GetOriginalAddress(lnk), la->true_addr,
31205116753SAndrey V. Elsukov 		    GetAliasAddress(lnk), htons(FTP_CONTROL_PORT_NUMBER - 1),
31305116753SAndrey V. Elsukov 		    htons(la->true_port), GET_ALIAS_PORT, IPPROTO_TCP);
31405116753SAndrey V. Elsukov 	}
31505116753SAndrey V. Elsukov 	/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
31605116753SAndrey V. Elsukov 	if (dlen) {
31705116753SAndrey V. Elsukov 		sptr = (char *)pip;		/* start over at beginning */
31805116753SAndrey V. Elsukov 		tlen = ntohs(pip->ip_len);	/* recalc tlen, pkt may
319effc8e57SLutz Donnerhacke 						 * have grown. */
32005116753SAndrey V. Elsukov 		if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
32105116753SAndrey V. Elsukov 			pflags &= ~WAIT_CRLF;
32205116753SAndrey V. Elsukov 		else
32305116753SAndrey V. Elsukov 			pflags |= WAIT_CRLF;
32405116753SAndrey V. Elsukov 		SetProtocolFlags(lnk, pflags);
32505116753SAndrey V. Elsukov        }
32605116753SAndrey V. Elsukov }
32705116753SAndrey V. Elsukov 
32867b333b7SRuslan Ermilov static int
ParseFtpPortCommand(struct libalias * la,char * sptr,int dlen)3295e289f9eSPoul-Henning Kamp ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen)
3303b160b8bSBrian Somers {
33167b333b7SRuslan Ermilov 	char ch;
3323b160b8bSBrian Somers 	int i, state;
333b7666040SRuslan Ermilov 	u_int32_t addr;
334b7666040SRuslan Ermilov 	u_short port;
335b7666040SRuslan Ermilov 	u_int8_t octet;
3363b160b8bSBrian Somers 
337b7666040SRuslan Ermilov 	/* Format: "PORT A,D,D,R,PO,RT". */
338b7666040SRuslan Ermilov 
339b7666040SRuslan Ermilov 	/* Return if data length is too short. */
340b7666040SRuslan Ermilov 	if (dlen < 18)
341ffcb611aSDag-Erling Smørgrav 		return (0);
34276525129SRuslan Ermilov 
343b46d3e21SAlexander Motin 	if (strncasecmp("PORT ", sptr, 5))
344b46d3e21SAlexander Motin 		return (0);
345b46d3e21SAlexander Motin 
346b7666040SRuslan Ermilov 	addr = port = octet = 0;
347b46d3e21SAlexander Motin 	state = 0;
348b46d3e21SAlexander Motin 	for (i = 5; i < dlen; i++) {
3493b160b8bSBrian Somers 		ch = sptr[i];
350b7666040SRuslan Ermilov 		switch (state) {
3513b160b8bSBrian Somers 		case 0:
352b7666040SRuslan Ermilov 			if (isspace(ch))
353b7666040SRuslan Ermilov 				break;
35467b333b7SRuslan Ermilov 			else
355b7666040SRuslan Ermilov 				state++;
356f0f93429SDag-Erling Smørgrav 		case 1:
357f0f93429SDag-Erling Smørgrav 		case 3:
358f0f93429SDag-Erling Smørgrav 		case 5:
359f0f93429SDag-Erling Smørgrav 		case 7:
360f0f93429SDag-Erling Smørgrav 		case 9:
361f0f93429SDag-Erling Smørgrav 		case 11:
362b7666040SRuslan Ermilov 			if (isdigit(ch)) {
363b7666040SRuslan Ermilov 				octet = ch - '0';
364b7666040SRuslan Ermilov 				state++;
365b7666040SRuslan Ermilov 			} else
366ffcb611aSDag-Erling Smørgrav 				return (0);
36767b333b7SRuslan Ermilov 			break;
368f0f93429SDag-Erling Smørgrav 		case 2:
369f0f93429SDag-Erling Smørgrav 		case 4:
370f0f93429SDag-Erling Smørgrav 		case 6:
371f0f93429SDag-Erling Smørgrav 		case 8:
372b7666040SRuslan Ermilov 			if (isdigit(ch))
373b7666040SRuslan Ermilov 				octet = 10 * octet + ch - '0';
374b7666040SRuslan Ermilov 			else if (ch == ',') {
375b7666040SRuslan Ermilov 				addr = (addr << 8) + octet;
376b7666040SRuslan Ermilov 				state++;
377b7666040SRuslan Ermilov 			} else
378ffcb611aSDag-Erling Smørgrav 				return (0);
37967b333b7SRuslan Ermilov 			break;
380f0f93429SDag-Erling Smørgrav 		case 10:
381f0f93429SDag-Erling Smørgrav 		case 12:
382b7666040SRuslan Ermilov 			if (isdigit(ch))
383b7666040SRuslan Ermilov 				octet = 10 * octet + ch - '0';
384b7666040SRuslan Ermilov 			else if (ch == ',' || state == 12) {
385b7666040SRuslan Ermilov 				port = (port << 8) + octet;
386b7666040SRuslan Ermilov 				state++;
387b7666040SRuslan Ermilov 			} else
388ffcb611aSDag-Erling Smørgrav 				return (0);
38967b333b7SRuslan Ermilov 			break;
39067b333b7SRuslan Ermilov 		}
39167b333b7SRuslan Ermilov 	}
39267b333b7SRuslan Ermilov 
393b7666040SRuslan Ermilov 	if (state == 13) {
3945e289f9eSPoul-Henning Kamp 		la->true_addr.s_addr = htonl(addr);
3955e289f9eSPoul-Henning Kamp 		la->true_port = port;
396ffcb611aSDag-Erling Smørgrav 		return (1);
397b7666040SRuslan Ermilov 	} else
398ffcb611aSDag-Erling Smørgrav 		return (0);
3993b160b8bSBrian Somers }
4003b160b8bSBrian Somers 
40176525129SRuslan Ermilov static int
ParseFtpEprtCommand(struct libalias * la,char * sptr,int dlen)4025e289f9eSPoul-Henning Kamp ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen)
40376525129SRuslan Ermilov {
404b7666040SRuslan Ermilov 	char ch, delim;
40576525129SRuslan Ermilov 	int i, state;
406b7666040SRuslan Ermilov 	u_int32_t addr;
407b7666040SRuslan Ermilov 	u_short port;
408b7666040SRuslan Ermilov 	u_int8_t octet;
40976525129SRuslan Ermilov 
410b7666040SRuslan Ermilov 	/* Format: "EPRT |1|A.D.D.R|PORT|". */
41176525129SRuslan Ermilov 
412b7666040SRuslan Ermilov 	/* Return if data length is too short. */
413b7666040SRuslan Ermilov 	if (dlen < 18)
414ffcb611aSDag-Erling Smørgrav 		return (0);
41576525129SRuslan Ermilov 
416b46d3e21SAlexander Motin 	if (strncasecmp("EPRT ", sptr, 5))
417b46d3e21SAlexander Motin 		return (0);
418b46d3e21SAlexander Motin 
419b7666040SRuslan Ermilov 	addr = port = octet = 0;
420b7666040SRuslan Ermilov 	delim = '|';		/* XXX gcc -Wuninitialized */
421b46d3e21SAlexander Motin 	state = 0;
422b46d3e21SAlexander Motin 	for (i = 5; i < dlen; i++) {
423b7666040SRuslan Ermilov 		ch = sptr[i];
424f0f93429SDag-Erling Smørgrav 		switch (state) {
425b7666040SRuslan Ermilov 		case 0:
426b7666040SRuslan Ermilov 			if (!isspace(ch)) {
427b7666040SRuslan Ermilov 				delim = ch;
428b7666040SRuslan Ermilov 				state++;
429b7666040SRuslan Ermilov 			}
430b7666040SRuslan Ermilov 			break;
431b7666040SRuslan Ermilov 		case 1:
432b7666040SRuslan Ermilov 			if (ch == '1')	/* IPv4 address */
433b7666040SRuslan Ermilov 				state++;
434b7666040SRuslan Ermilov 			else
435ffcb611aSDag-Erling Smørgrav 				return (0);
436b7666040SRuslan Ermilov 			break;
437b7666040SRuslan Ermilov 		case 2:
438b7666040SRuslan Ermilov 			if (ch == delim)
439b7666040SRuslan Ermilov 				state++;
440b7666040SRuslan Ermilov 			else
441ffcb611aSDag-Erling Smørgrav 				return (0);
442b7666040SRuslan Ermilov 			break;
443f0f93429SDag-Erling Smørgrav 		case 3:
444f0f93429SDag-Erling Smørgrav 		case 5:
445f0f93429SDag-Erling Smørgrav 		case 7:
446f0f93429SDag-Erling Smørgrav 		case 9:
447b7666040SRuslan Ermilov 			if (isdigit(ch)) {
448b7666040SRuslan Ermilov 				octet = ch - '0';
449b7666040SRuslan Ermilov 				state++;
450b7666040SRuslan Ermilov 			} else
451ffcb611aSDag-Erling Smørgrav 				return (0);
452b7666040SRuslan Ermilov 			break;
453f0f93429SDag-Erling Smørgrav 		case 4:
454f0f93429SDag-Erling Smørgrav 		case 6:
455f0f93429SDag-Erling Smørgrav 		case 8:
456f0f93429SDag-Erling Smørgrav 		case 10:
457b7666040SRuslan Ermilov 			if (isdigit(ch))
458b7666040SRuslan Ermilov 				octet = 10 * octet + ch - '0';
459b7666040SRuslan Ermilov 			else if (ch == '.' || state == 10) {
460b7666040SRuslan Ermilov 				addr = (addr << 8) + octet;
461b7666040SRuslan Ermilov 				state++;
462b7666040SRuslan Ermilov 			} else
463ffcb611aSDag-Erling Smørgrav 				return (0);
464b7666040SRuslan Ermilov 			break;
465b7666040SRuslan Ermilov 		case 11:
466b7666040SRuslan Ermilov 			if (isdigit(ch)) {
467b7666040SRuslan Ermilov 				port = ch - '0';
468b7666040SRuslan Ermilov 				state++;
469b7666040SRuslan Ermilov 			} else
470ffcb611aSDag-Erling Smørgrav 				return (0);
471b7666040SRuslan Ermilov 			break;
472b7666040SRuslan Ermilov 		case 12:
473b7666040SRuslan Ermilov 			if (isdigit(ch))
474b7666040SRuslan Ermilov 				port = 10 * port + ch - '0';
475b7666040SRuslan Ermilov 			else if (ch == delim)
476b7666040SRuslan Ermilov 				state++;
477b7666040SRuslan Ermilov 			else
478ffcb611aSDag-Erling Smørgrav 				return (0);
479b7666040SRuslan Ermilov 			break;
480b7666040SRuslan Ermilov 		}
481b7666040SRuslan Ermilov 	}
482b7666040SRuslan Ermilov 
483b7666040SRuslan Ermilov 	if (state == 13) {
4845e289f9eSPoul-Henning Kamp 		la->true_addr.s_addr = htonl(addr);
4855e289f9eSPoul-Henning Kamp 		la->true_port = port;
486ffcb611aSDag-Erling Smørgrav 		return (1);
487b7666040SRuslan Ermilov 	} else
488ffcb611aSDag-Erling Smørgrav 		return (0);
489b7666040SRuslan Ermilov }
490b7666040SRuslan Ermilov 
491b7666040SRuslan Ermilov static int
ParseFtp227Reply(struct libalias * la,char * sptr,int dlen)4925e289f9eSPoul-Henning Kamp ParseFtp227Reply(struct libalias *la, char *sptr, int dlen)
493b7666040SRuslan Ermilov {
494b7666040SRuslan Ermilov 	char ch;
495b7666040SRuslan Ermilov 	int i, state;
496b7666040SRuslan Ermilov 	u_int32_t addr;
497b7666040SRuslan Ermilov 	u_short port;
498b7666040SRuslan Ermilov 	u_int8_t octet;
499b7666040SRuslan Ermilov 
500b7666040SRuslan Ermilov 	/* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
501b7666040SRuslan Ermilov 
502b7666040SRuslan Ermilov 	/* Return if data length is too short. */
503b7666040SRuslan Ermilov 	if (dlen < 17)
504ffcb611aSDag-Erling Smørgrav 		return (0);
505b7666040SRuslan Ermilov 
506b46d3e21SAlexander Motin 	if (strncmp("227 ", sptr, 4))
507b46d3e21SAlexander Motin 		return (0);
508b46d3e21SAlexander Motin 
509b7666040SRuslan Ermilov 	addr = port = octet = 0;
510b7666040SRuslan Ermilov 
511b46d3e21SAlexander Motin 	state = 0;
512b46d3e21SAlexander Motin 	for (i = 4; i < dlen; i++) {
51376525129SRuslan Ermilov 		ch = sptr[i];
514f0f93429SDag-Erling Smørgrav 		switch (state) {
51576525129SRuslan Ermilov 		case 0:
516b7666040SRuslan Ermilov 			if (ch == '(')
517b7666040SRuslan Ermilov 				state++;
518b7666040SRuslan Ermilov 			break;
519f0f93429SDag-Erling Smørgrav 		case 1:
520f0f93429SDag-Erling Smørgrav 		case 3:
521f0f93429SDag-Erling Smørgrav 		case 5:
522f0f93429SDag-Erling Smørgrav 		case 7:
523f0f93429SDag-Erling Smørgrav 		case 9:
524f0f93429SDag-Erling Smørgrav 		case 11:
525b7666040SRuslan Ermilov 			if (isdigit(ch)) {
526b7666040SRuslan Ermilov 				octet = ch - '0';
527b7666040SRuslan Ermilov 				state++;
528b7666040SRuslan Ermilov 			} else
529ffcb611aSDag-Erling Smørgrav 				return (0);
530b7666040SRuslan Ermilov 			break;
531f0f93429SDag-Erling Smørgrav 		case 2:
532f0f93429SDag-Erling Smørgrav 		case 4:
533f0f93429SDag-Erling Smørgrav 		case 6:
534f0f93429SDag-Erling Smørgrav 		case 8:
535b7666040SRuslan Ermilov 			if (isdigit(ch))
536b7666040SRuslan Ermilov 				octet = 10 * octet + ch - '0';
537b7666040SRuslan Ermilov 			else if (ch == ',') {
538b7666040SRuslan Ermilov 				addr = (addr << 8) + octet;
539b7666040SRuslan Ermilov 				state++;
540b7666040SRuslan Ermilov 			} else
541ffcb611aSDag-Erling Smørgrav 				return (0);
542b7666040SRuslan Ermilov 			break;
543f0f93429SDag-Erling Smørgrav 		case 10:
544f0f93429SDag-Erling Smørgrav 		case 12:
545b7666040SRuslan Ermilov 			if (isdigit(ch))
546b7666040SRuslan Ermilov 				octet = 10 * octet + ch - '0';
547b7666040SRuslan Ermilov 			else if (ch == ',' || (state == 12 && ch == ')')) {
548b7666040SRuslan Ermilov 				port = (port << 8) + octet;
549b7666040SRuslan Ermilov 				state++;
550b7666040SRuslan Ermilov 			} else
551ffcb611aSDag-Erling Smørgrav 				return (0);
552b7666040SRuslan Ermilov 			break;
55376525129SRuslan Ermilov 		}
55476525129SRuslan Ermilov 	}
55576525129SRuslan Ermilov 
556b7666040SRuslan Ermilov 	if (state == 13) {
5575e289f9eSPoul-Henning Kamp 		la->true_port = port;
5585e289f9eSPoul-Henning Kamp 		la->true_addr.s_addr = htonl(addr);
559ffcb611aSDag-Erling Smørgrav 		return (1);
560b7666040SRuslan Ermilov 	} else
561ffcb611aSDag-Erling Smørgrav 		return (0);
56276525129SRuslan Ermilov }
563b7666040SRuslan Ermilov 
564b7666040SRuslan Ermilov static int
ParseFtp229Reply(struct libalias * la,char * sptr,int dlen)5655e289f9eSPoul-Henning Kamp ParseFtp229Reply(struct libalias *la, char *sptr, int dlen)
566b7666040SRuslan Ermilov {
567b7666040SRuslan Ermilov 	char ch, delim;
568b7666040SRuslan Ermilov 	int i, state;
569b7666040SRuslan Ermilov 	u_short port;
570b7666040SRuslan Ermilov 
571b7666040SRuslan Ermilov 	/* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
572b7666040SRuslan Ermilov 
573b7666040SRuslan Ermilov 	/* Return if data length is too short. */
574b7666040SRuslan Ermilov 	if (dlen < 11)
575ffcb611aSDag-Erling Smørgrav 		return (0);
576b7666040SRuslan Ermilov 
577b46d3e21SAlexander Motin 	if (strncmp("229 ", sptr, 4))
578b46d3e21SAlexander Motin 		return (0);
579b46d3e21SAlexander Motin 
580b7666040SRuslan Ermilov 	port = 0;
581b7666040SRuslan Ermilov 	delim = '|';		/* XXX gcc -Wuninitialized */
582b7666040SRuslan Ermilov 
583b46d3e21SAlexander Motin 	state = 0;
584b46d3e21SAlexander Motin 	for (i = 4; i < dlen; i++) {
585b7666040SRuslan Ermilov 		ch = sptr[i];
586f0f93429SDag-Erling Smørgrav 		switch (state) {
587b7666040SRuslan Ermilov 		case 0:
588b7666040SRuslan Ermilov 			if (ch == '(')
589b7666040SRuslan Ermilov 				state++;
590b7666040SRuslan Ermilov 			break;
591b7666040SRuslan Ermilov 		case 1:
592b7666040SRuslan Ermilov 			delim = ch;
593b7666040SRuslan Ermilov 			state++;
594b7666040SRuslan Ermilov 			break;
595f0f93429SDag-Erling Smørgrav 		case 2:
596f0f93429SDag-Erling Smørgrav 		case 3:
597b7666040SRuslan Ermilov 			if (ch == delim)
598b7666040SRuslan Ermilov 				state++;
59976525129SRuslan Ermilov 			else
600ffcb611aSDag-Erling Smørgrav 				return (0);
601b7666040SRuslan Ermilov 			break;
602b7666040SRuslan Ermilov 		case 4:
603b7666040SRuslan Ermilov 			if (isdigit(ch)) {
604b7666040SRuslan Ermilov 				port = ch - '0';
605b7666040SRuslan Ermilov 				state++;
606b7666040SRuslan Ermilov 			} else
607ffcb611aSDag-Erling Smørgrav 				return (0);
608b7666040SRuslan Ermilov 			break;
609b7666040SRuslan Ermilov 		case 5:
610b7666040SRuslan Ermilov 			if (isdigit(ch))
611b7666040SRuslan Ermilov 				port = 10 * port + ch - '0';
612b7666040SRuslan Ermilov 			else if (ch == delim)
613b7666040SRuslan Ermilov 				state++;
614b7666040SRuslan Ermilov 			else
615ffcb611aSDag-Erling Smørgrav 				return (0);
616b7666040SRuslan Ermilov 			break;
617b7666040SRuslan Ermilov 		case 6:
618b7666040SRuslan Ermilov 			if (ch == ')')
619b7666040SRuslan Ermilov 				state++;
620b7666040SRuslan Ermilov 			else
621ffcb611aSDag-Erling Smørgrav 				return (0);
622b7666040SRuslan Ermilov 			break;
623b7666040SRuslan Ermilov 		}
624b7666040SRuslan Ermilov 	}
625b7666040SRuslan Ermilov 
626b7666040SRuslan Ermilov 	if (state == 7) {
6275e289f9eSPoul-Henning Kamp 		la->true_port = port;
628ffcb611aSDag-Erling Smørgrav 		return (1);
629b7666040SRuslan Ermilov 	} else
630ffcb611aSDag-Erling Smørgrav 		return (0);
63176525129SRuslan Ermilov }
63276525129SRuslan Ermilov 
6333b160b8bSBrian Somers static void
NewFtpMessage(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize,int ftp_message_type)6345e289f9eSPoul-Henning Kamp NewFtpMessage(struct libalias *la, struct ip *pip,
635ed01a582SDag-Erling Smørgrav     struct alias_link *lnk,
63667b333b7SRuslan Ermilov     int maxpacketsize,
637b7666040SRuslan Ermilov     int ftp_message_type)
6383b160b8bSBrian Somers {
639ed01a582SDag-Erling Smørgrav 	struct alias_link *ftp_lnk;
6403b160b8bSBrian Somers 
641b7666040SRuslan Ermilov 	/* Security checks. */
6425e289f9eSPoul-Henning Kamp 	if (pip->ip_src.s_addr != la->true_addr.s_addr)
643b7666040SRuslan Ermilov 		return;
644b7666040SRuslan Ermilov 
6455e289f9eSPoul-Henning Kamp 	if (la->true_port < IPPORT_RESERVED)
646b7666040SRuslan Ermilov 		return;
647b7666040SRuslan Ermilov 
648b7666040SRuslan Ermilov 	/* Establish link to address and port found in FTP control message. */
64905116753SAndrey V. Elsukov 	ftp_lnk = AddLink(la, la->true_addr, GetDestAddress(lnk),
65005116753SAndrey V. Elsukov 	    GetAliasAddress(lnk), htons(la->true_port), 0, GET_ALIAS_PORT,
65105116753SAndrey V. Elsukov 	    IPPROTO_TCP);
6523b160b8bSBrian Somers 
653ed01a582SDag-Erling Smørgrav 	if (ftp_lnk != NULL) {
6543b160b8bSBrian Somers 		int slen, hlen, tlen, dlen;
6553b160b8bSBrian Somers 		struct tcphdr *tc;
6563b160b8bSBrian Somers 
657b5ce85feSBrian Somers #ifndef NO_FW_PUNCH
6588ddc51bcSEivind Eklund 		/* Punch hole in firewall */
659ed01a582SDag-Erling Smørgrav 		PunchFWHole(ftp_lnk);
660b5ce85feSBrian Somers #endif
6618ddc51bcSEivind Eklund 
6623b160b8bSBrian Somers 		/* Calculate data length of TCP packet */
6639fa0fd26SDag-Erling Smørgrav 		tc = (struct tcphdr *)ip_next(pip);
6643b160b8bSBrian Somers 		hlen = (pip->ip_hl + tc->th_off) << 2;
6653b160b8bSBrian Somers 		tlen = ntohs(pip->ip_len);
6663b160b8bSBrian Somers 		dlen = tlen - hlen;
6673b160b8bSBrian Somers 
668b7666040SRuslan Ermilov 		/* Create new FTP message. */
6693b160b8bSBrian Somers 		{
670b7666040SRuslan Ermilov 			char stemp[MAX_MESSAGE_SIZE + 1];
6713b160b8bSBrian Somers 			char *sptr;
6723b160b8bSBrian Somers 			u_short alias_port;
6733b160b8bSBrian Somers 			u_char *ptr;
6743b160b8bSBrian Somers 			int a1, a2, a3, a4, p1, p2;
6753b160b8bSBrian Somers 			struct in_addr alias_address;
6763b160b8bSBrian Somers 
6773b160b8bSBrian Somers 			/* Decompose alias address into quad format */
678ed01a582SDag-Erling Smørgrav 			alias_address = GetAliasAddress(lnk);
6798ddc51bcSEivind Eklund 			ptr = (u_char *)&alias_address.s_addr;
680f0f93429SDag-Erling Smørgrav 			a1 = *ptr++;
681f0f93429SDag-Erling Smørgrav 			a2 = *ptr++;
682f0f93429SDag-Erling Smørgrav 			a3 = *ptr++;
683f0f93429SDag-Erling Smørgrav 			a4 = *ptr;
6843b160b8bSBrian Somers 
685ed01a582SDag-Erling Smørgrav 			alias_port = GetAliasPort(ftp_lnk);
68667b333b7SRuslan Ermilov 
687b46d3e21SAlexander Motin 			/* Prepare new command */
688f0f93429SDag-Erling Smørgrav 			switch (ftp_message_type) {
689b7666040SRuslan Ermilov 			case FTP_PORT_COMMAND:
690b7666040SRuslan Ermilov 			case FTP_227_REPLY:
691b7666040SRuslan Ermilov 				/* Decompose alias port into pair format. */
6923b160b8bSBrian Somers 				ptr = (char *)&alias_port;
693f0f93429SDag-Erling Smørgrav 				p1 = *ptr++;
694f0f93429SDag-Erling Smørgrav 				p2 = *ptr;
6953b160b8bSBrian Somers 
696b7666040SRuslan Ermilov 				if (ftp_message_type == FTP_PORT_COMMAND) {
697b7666040SRuslan Ermilov 					/* Generate PORT command string. */
6983b160b8bSBrian Somers 					sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
6993b160b8bSBrian Somers 					    a1, a2, a3, a4, p1, p2);
700b7666040SRuslan Ermilov 				} else {
701b7666040SRuslan Ermilov 					/* Generate 227 reply string. */
702b7666040SRuslan Ermilov 					sprintf(stemp,
703b7666040SRuslan Ermilov 					    "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
70476525129SRuslan Ermilov 					    a1, a2, a3, a4, p1, p2);
705b7666040SRuslan Ermilov 				}
706b7666040SRuslan Ermilov 				break;
707b7666040SRuslan Ermilov 			case FTP_EPRT_COMMAND:
708b7666040SRuslan Ermilov 				/* Generate EPRT command string. */
709b7666040SRuslan Ermilov 				sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
710b7666040SRuslan Ermilov 				    a1, a2, a3, a4, ntohs(alias_port));
711b7666040SRuslan Ermilov 				break;
712b7666040SRuslan Ermilov 			case FTP_229_REPLY:
713b7666040SRuslan Ermilov 				/* Generate 229 reply string. */
714b7666040SRuslan Ermilov 				sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
715b7666040SRuslan Ermilov 				    ntohs(alias_port));
716b7666040SRuslan Ermilov 				break;
717b7666040SRuslan Ermilov 			}
71876525129SRuslan Ermilov 
71976525129SRuslan Ermilov 			/* Save string length for IP header modification */
72076525129SRuslan Ermilov 			slen = strlen(stemp);
72176525129SRuslan Ermilov 
722b7666040SRuslan Ermilov 			/* Copy modified buffer into IP packet. */
723f0f93429SDag-Erling Smørgrav 			sptr = (char *)pip;
724f0f93429SDag-Erling Smørgrav 			sptr += hlen;
72576525129SRuslan Ermilov 			strncpy(sptr, stemp, maxpacketsize - hlen);
72676525129SRuslan Ermilov 		}
72776525129SRuslan Ermilov 
72876525129SRuslan Ermilov 		/* Save information regarding modified seq and ack numbers */
72976525129SRuslan Ermilov 		{
73076525129SRuslan Ermilov 			int delta;
73176525129SRuslan Ermilov 
732ed01a582SDag-Erling Smørgrav 			SetAckModified(lnk);
7334741f3a1SPaolo Pisati 			tc = (struct tcphdr *)ip_next(pip);
7344741f3a1SPaolo Pisati 			delta = GetDeltaSeqOut(tc->th_seq, lnk);
7354741f3a1SPaolo Pisati 			AddSeq(lnk, delta + slen - dlen, pip->ip_hl,
7364741f3a1SPaolo Pisati 			    pip->ip_len, tc->th_seq, tc->th_off);
73776525129SRuslan Ermilov 		}
73876525129SRuslan Ermilov 
73976525129SRuslan Ermilov 		/* Revise IP header */
74076525129SRuslan Ermilov 		{
74176525129SRuslan Ermilov 			u_short new_len;
74276525129SRuslan Ermilov 
74346701f31SEd Maste 			new_len = htons(hlen +
74446701f31SEd Maste 			    MIN(slen, maxpacketsize - hlen));
74576525129SRuslan Ermilov 			DifferentialChecksum(&pip->ip_sum,
74676525129SRuslan Ermilov 			    &new_len,
74776525129SRuslan Ermilov 			    &pip->ip_len,
74876525129SRuslan Ermilov 			    1);
74976525129SRuslan Ermilov 			pip->ip_len = new_len;
75076525129SRuslan Ermilov 		}
75176525129SRuslan Ermilov 
75276525129SRuslan Ermilov 		/* Compute TCP checksum for revised packet */
75376525129SRuslan Ermilov 		tc->th_sum = 0;
75459dde15eSGleb Smirnoff #ifdef _KERNEL
755*0fc7bdc9SRichard Scheffenegger 		tcp_set_flags(tc, tcp_get_flags(tc) | TH_RES1);
75659dde15eSGleb Smirnoff #else
75776525129SRuslan Ermilov 		tc->th_sum = TcpChecksum(pip);
75859dde15eSGleb Smirnoff #endif
759f0f93429SDag-Erling Smørgrav 	} else {
7606293e003SGleb Smirnoff #ifdef LIBALIAS_DEBUG
76176525129SRuslan Ermilov 		fprintf(stderr,
762b7666040SRuslan Ermilov 		    "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
7634c32f5d2SBrian Somers #endif
7643b160b8bSBrian Somers 	}
7653b160b8bSBrian Somers }
766