1a9643ea8Slogwang /*-
2*22ce4affSfengbojiang  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang  *
4a9643ea8Slogwang  * Copyright (c) 2001 Charles Mott <[email protected]>
5a9643ea8Slogwang  * All rights reserved.
6a9643ea8Slogwang  *
7a9643ea8Slogwang  * Redistribution and use in source and binary forms, with or without
8a9643ea8Slogwang  * modification, are permitted provided that the following conditions
9a9643ea8Slogwang  * are met:
10a9643ea8Slogwang  * 1. Redistributions of source code must retain the above copyright
11a9643ea8Slogwang  *    notice, this list of conditions and the following disclaimer.
12a9643ea8Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
13a9643ea8Slogwang  *    notice, this list of conditions and the following disclaimer in the
14a9643ea8Slogwang  *    documentation and/or other materials provided with the distribution.
15a9643ea8Slogwang  *
16a9643ea8Slogwang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17a9643ea8Slogwang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18a9643ea8Slogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19a9643ea8Slogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20a9643ea8Slogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21a9643ea8Slogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22a9643ea8Slogwang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23a9643ea8Slogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24a9643ea8Slogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25a9643ea8Slogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26a9643ea8Slogwang  * SUCH DAMAGE.
27a9643ea8Slogwang  */
28a9643ea8Slogwang 
29a9643ea8Slogwang #include <sys/cdefs.h>
30a9643ea8Slogwang __FBSDID("$FreeBSD$");
31a9643ea8Slogwang 
32a9643ea8Slogwang /*
33a9643ea8Slogwang     Alias_ftp.c performs special processing for FTP sessions under
34a9643ea8Slogwang     TCP.  Specifically, when a PORT/EPRT command from the client
35a9643ea8Slogwang     side or 227/229 reply from the server is sent, it is intercepted
36a9643ea8Slogwang     and modified.  The address is changed to the gateway machine
37a9643ea8Slogwang     and an aliasing port is used.
38a9643ea8Slogwang 
39a9643ea8Slogwang     For this routine to work, the message must fit entirely into a
40a9643ea8Slogwang     single TCP packet.  This is typically the case, but exceptions
41a9643ea8Slogwang     can easily be envisioned under the actual specifications.
42a9643ea8Slogwang 
43a9643ea8Slogwang     Probably the most troubling aspect of the approach taken here is
44a9643ea8Slogwang     that the new message will typically be a different length, and
45a9643ea8Slogwang     this causes a certain amount of bookkeeping to keep track of the
46a9643ea8Slogwang     changes of sequence and acknowledgment numbers, since the client
47a9643ea8Slogwang     machine is totally unaware of the modification to the TCP stream.
48a9643ea8Slogwang 
49a9643ea8Slogwang     References: RFC 959, RFC 2428.
50a9643ea8Slogwang 
51a9643ea8Slogwang     Initial version:  August, 1996  (cjm)
52a9643ea8Slogwang 
53a9643ea8Slogwang     Version 1.6
54a9643ea8Slogwang 	 Brian Somers and Martin Renters identified an IP checksum
55a9643ea8Slogwang 	 error for modified IP packets.
56a9643ea8Slogwang 
57a9643ea8Slogwang     Version 1.7:  January 9, 1996 (cjm)
58a9643ea8Slogwang 	 Differential checksum computation for change
59a9643ea8Slogwang 	 in IP packet length.
60a9643ea8Slogwang 
61a9643ea8Slogwang     Version 2.1:  May, 1997 (cjm)
62a9643ea8Slogwang 	 Very minor changes to conform with
63a9643ea8Slogwang 	 local/global/function naming conventions
64a9643ea8Slogwang 	 within the packet aliasing module.
65a9643ea8Slogwang 
66a9643ea8Slogwang     Version 3.1:  May, 2000 (eds)
67a9643ea8Slogwang 	 Add support for passive mode, alias the 227 replies.
68a9643ea8Slogwang 
69a9643ea8Slogwang     See HISTORY file for record of revisions.
70a9643ea8Slogwang */
71a9643ea8Slogwang 
72a9643ea8Slogwang /* Includes */
73a9643ea8Slogwang #ifdef _KERNEL
74a9643ea8Slogwang #include <sys/param.h>
75a9643ea8Slogwang #include <sys/ctype.h>
76a9643ea8Slogwang #include <sys/systm.h>
77a9643ea8Slogwang #include <sys/kernel.h>
78a9643ea8Slogwang #include <sys/module.h>
79a9643ea8Slogwang #else
80a9643ea8Slogwang #include <ctype.h>
81a9643ea8Slogwang #include <errno.h>
82a9643ea8Slogwang #include <sys/types.h>
83a9643ea8Slogwang #include <stdio.h>
84a9643ea8Slogwang #include <string.h>
85a9643ea8Slogwang #endif
86a9643ea8Slogwang 
87a9643ea8Slogwang #include <netinet/in_systm.h>
88a9643ea8Slogwang #include <netinet/in.h>
89a9643ea8Slogwang #include <netinet/ip.h>
90a9643ea8Slogwang #include <netinet/tcp.h>
91a9643ea8Slogwang 
92a9643ea8Slogwang #ifdef _KERNEL
93a9643ea8Slogwang #include <netinet/libalias/alias.h>
94a9643ea8Slogwang #include <netinet/libalias/alias_local.h>
95a9643ea8Slogwang #include <netinet/libalias/alias_mod.h>
96a9643ea8Slogwang #else
97a9643ea8Slogwang #include "alias_local.h"
98a9643ea8Slogwang #include "alias_mod.h"
99a9643ea8Slogwang #endif
100a9643ea8Slogwang 
101a9643ea8Slogwang #define FTP_CONTROL_PORT_NUMBER 21
102a9643ea8Slogwang 
103a9643ea8Slogwang static void
104a9643ea8Slogwang AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *,
105a9643ea8Slogwang     int maxpacketsize);
106a9643ea8Slogwang static void
107a9643ea8Slogwang AliasHandleFtpIn(struct libalias *, struct ip *, struct alias_link *);
108a9643ea8Slogwang 
109a9643ea8Slogwang static int
fingerprint_out(struct libalias * la,struct alias_data * ah)110a9643ea8Slogwang fingerprint_out(struct libalias *la, struct alias_data *ah)
111a9643ea8Slogwang {
112a9643ea8Slogwang 
113a9643ea8Slogwang 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
114a9643ea8Slogwang 	    ah->maxpktsize == 0)
115a9643ea8Slogwang 		return (-1);
116a9643ea8Slogwang 	if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
117a9643ea8Slogwang 	    ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
118a9643ea8Slogwang 		return (0);
119a9643ea8Slogwang 	return (-1);
120a9643ea8Slogwang }
121a9643ea8Slogwang 
122a9643ea8Slogwang static int
fingerprint_in(struct libalias * la,struct alias_data * ah)123a9643ea8Slogwang fingerprint_in(struct libalias *la, struct alias_data *ah)
124a9643ea8Slogwang {
125a9643ea8Slogwang 
126a9643ea8Slogwang 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
127a9643ea8Slogwang 		return (-1);
128a9643ea8Slogwang 	if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
129a9643ea8Slogwang 	    ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
130a9643ea8Slogwang 		return (0);
131a9643ea8Slogwang 	return (-1);
132a9643ea8Slogwang }
133a9643ea8Slogwang 
134a9643ea8Slogwang static int
protohandler_out(struct libalias * la,struct ip * pip,struct alias_data * ah)135a9643ea8Slogwang protohandler_out(struct libalias *la, struct ip *pip, struct alias_data *ah)
136a9643ea8Slogwang {
137a9643ea8Slogwang 
138a9643ea8Slogwang 	AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
139a9643ea8Slogwang 	return (0);
140a9643ea8Slogwang }
141a9643ea8Slogwang 
142a9643ea8Slogwang static int
protohandler_in(struct libalias * la,struct ip * pip,struct alias_data * ah)143a9643ea8Slogwang protohandler_in(struct libalias *la, struct ip *pip, struct alias_data *ah)
144a9643ea8Slogwang {
145a9643ea8Slogwang 
146a9643ea8Slogwang 	AliasHandleFtpIn(la, pip, ah->lnk);
147a9643ea8Slogwang 	return (0);
148a9643ea8Slogwang }
149a9643ea8Slogwang 
150a9643ea8Slogwang struct proto_handler handlers[] = {
151a9643ea8Slogwang 	{
152a9643ea8Slogwang 	  .pri = 80,
153a9643ea8Slogwang 	  .dir = OUT,
154a9643ea8Slogwang 	  .proto = TCP,
155a9643ea8Slogwang 	  .fingerprint = &fingerprint_out,
156a9643ea8Slogwang 	  .protohandler = &protohandler_out
157a9643ea8Slogwang 	},
158a9643ea8Slogwang 	{
159a9643ea8Slogwang 	  .pri = 80,
160a9643ea8Slogwang 	  .dir = IN,
161a9643ea8Slogwang 	  .proto = TCP,
162a9643ea8Slogwang 	  .fingerprint = &fingerprint_in,
163a9643ea8Slogwang 	  .protohandler = &protohandler_in
164a9643ea8Slogwang 	},
165a9643ea8Slogwang 	{ EOH }
166a9643ea8Slogwang };
167a9643ea8Slogwang 
168a9643ea8Slogwang static int
mod_handler(module_t mod,int type,void * data)169a9643ea8Slogwang mod_handler(module_t mod, int type, void *data)
170a9643ea8Slogwang {
171a9643ea8Slogwang 	int error;
172a9643ea8Slogwang 
173a9643ea8Slogwang 	switch (type) {
174a9643ea8Slogwang 	case MOD_LOAD:
175a9643ea8Slogwang 		error = 0;
176a9643ea8Slogwang 		LibAliasAttachHandlers(handlers);
177a9643ea8Slogwang 		break;
178a9643ea8Slogwang 	case MOD_UNLOAD:
179a9643ea8Slogwang 		error = 0;
180a9643ea8Slogwang 		LibAliasDetachHandlers(handlers);
181a9643ea8Slogwang 		break;
182a9643ea8Slogwang 	default:
183a9643ea8Slogwang 		error = EINVAL;
184a9643ea8Slogwang 	}
185a9643ea8Slogwang 	return (error);
186a9643ea8Slogwang }
187a9643ea8Slogwang 
188a9643ea8Slogwang #ifdef _KERNEL
189a9643ea8Slogwang static
190a9643ea8Slogwang #endif
191a9643ea8Slogwang moduledata_t alias_mod = {
192a9643ea8Slogwang        "alias_ftp", mod_handler, NULL
193a9643ea8Slogwang };
194a9643ea8Slogwang 
195a9643ea8Slogwang #ifdef	_KERNEL
196a9643ea8Slogwang DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
197a9643ea8Slogwang MODULE_VERSION(alias_ftp, 1);
198a9643ea8Slogwang MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1);
199a9643ea8Slogwang #endif
200a9643ea8Slogwang 
201a9643ea8Slogwang #define FTP_CONTROL_PORT_NUMBER 21
202a9643ea8Slogwang #define MAX_MESSAGE_SIZE	128
203a9643ea8Slogwang 
204a9643ea8Slogwang /* FTP protocol flags. */
205a9643ea8Slogwang #define WAIT_CRLF		0x01
206a9643ea8Slogwang 
207a9643ea8Slogwang enum ftp_message_type {
208a9643ea8Slogwang 	FTP_PORT_COMMAND,
209a9643ea8Slogwang 	FTP_EPRT_COMMAND,
210a9643ea8Slogwang 	FTP_227_REPLY,
211a9643ea8Slogwang 	FTP_229_REPLY,
212a9643ea8Slogwang 	FTP_UNKNOWN_MESSAGE
213a9643ea8Slogwang };
214a9643ea8Slogwang 
215a9643ea8Slogwang static int	ParseFtpPortCommand(struct libalias *la, char *, int);
216a9643ea8Slogwang static int	ParseFtpEprtCommand(struct libalias *la, char *, int);
217a9643ea8Slogwang static int	ParseFtp227Reply(struct libalias *la, char *, int);
218a9643ea8Slogwang static int	ParseFtp229Reply(struct libalias *la, char *, int);
219a9643ea8Slogwang static void	NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int);
220a9643ea8Slogwang 
221a9643ea8Slogwang static void
AliasHandleFtpOut(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize)222a9643ea8Slogwang AliasHandleFtpOut(
223a9643ea8Slogwang     struct libalias *la,
224a9643ea8Slogwang     struct ip *pip,		/* IP packet to examine/patch */
225a9643ea8Slogwang     struct alias_link *lnk,	/* The link to go through (aliased port) */
226a9643ea8Slogwang     int maxpacketsize		/* The maximum size this packet can grow to
227a9643ea8Slogwang 	(including headers) */ )
228a9643ea8Slogwang {
229a9643ea8Slogwang 	int hlen, tlen, dlen, pflags;
230a9643ea8Slogwang 	char *sptr;
231a9643ea8Slogwang 	struct tcphdr *tc;
232a9643ea8Slogwang 	int ftp_message_type;
233a9643ea8Slogwang 
234a9643ea8Slogwang /* Calculate data length of TCP packet */
235a9643ea8Slogwang 	tc = (struct tcphdr *)ip_next(pip);
236a9643ea8Slogwang 	hlen = (pip->ip_hl + tc->th_off) << 2;
237a9643ea8Slogwang 	tlen = ntohs(pip->ip_len);
238a9643ea8Slogwang 	dlen = tlen - hlen;
239a9643ea8Slogwang 
240a9643ea8Slogwang /* Place string pointer and beginning of data */
241a9643ea8Slogwang 	sptr = (char *)pip;
242a9643ea8Slogwang 	sptr += hlen;
243a9643ea8Slogwang 
244a9643ea8Slogwang /*
245a9643ea8Slogwang  * Check that data length is not too long and previous message was
246a9643ea8Slogwang  * properly terminated with CRLF.
247a9643ea8Slogwang  */
248a9643ea8Slogwang 	pflags = GetProtocolFlags(lnk);
249a9643ea8Slogwang 	if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) {
250a9643ea8Slogwang 		ftp_message_type = FTP_UNKNOWN_MESSAGE;
251a9643ea8Slogwang 
252a9643ea8Slogwang 		if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
253a9643ea8Slogwang /*
254a9643ea8Slogwang  * When aliasing a client, check for the PORT/EPRT command.
255a9643ea8Slogwang  */
256a9643ea8Slogwang 			if (ParseFtpPortCommand(la, sptr, dlen))
257a9643ea8Slogwang 				ftp_message_type = FTP_PORT_COMMAND;
258a9643ea8Slogwang 			else if (ParseFtpEprtCommand(la, sptr, dlen))
259a9643ea8Slogwang 				ftp_message_type = FTP_EPRT_COMMAND;
260a9643ea8Slogwang 		} else {
261a9643ea8Slogwang /*
262a9643ea8Slogwang  * When aliasing a server, check for the 227/229 reply.
263a9643ea8Slogwang  */
264a9643ea8Slogwang 			if (ParseFtp227Reply(la, sptr, dlen))
265a9643ea8Slogwang 				ftp_message_type = FTP_227_REPLY;
266a9643ea8Slogwang 			else if (ParseFtp229Reply(la, sptr, dlen)) {
267a9643ea8Slogwang 				ftp_message_type = FTP_229_REPLY;
268a9643ea8Slogwang 				la->true_addr.s_addr = pip->ip_src.s_addr;
269a9643ea8Slogwang 			}
270a9643ea8Slogwang 		}
271a9643ea8Slogwang 
272a9643ea8Slogwang 		if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
273a9643ea8Slogwang 			NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type);
274a9643ea8Slogwang 	}
275a9643ea8Slogwang /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
276a9643ea8Slogwang 
277a9643ea8Slogwang 	if (dlen) {		/* only if there's data */
278a9643ea8Slogwang 		sptr = (char *)pip;	/* start over at beginning */
279a9643ea8Slogwang 		tlen = ntohs(pip->ip_len);	/* recalc tlen, pkt may
280a9643ea8Slogwang 						 * have grown */
281a9643ea8Slogwang 		if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
282a9643ea8Slogwang 			pflags &= ~WAIT_CRLF;
283a9643ea8Slogwang 		else
284a9643ea8Slogwang 			pflags |= WAIT_CRLF;
285a9643ea8Slogwang 		SetProtocolFlags(lnk, pflags);
286a9643ea8Slogwang 	}
287a9643ea8Slogwang }
288a9643ea8Slogwang 
289a9643ea8Slogwang static void
AliasHandleFtpIn(struct libalias * la,struct ip * pip,struct alias_link * lnk)290a9643ea8Slogwang AliasHandleFtpIn(struct libalias *la,
291a9643ea8Slogwang     struct ip *pip,		/* IP packet to examine/patch */
292a9643ea8Slogwang     struct alias_link *lnk)	/* The link to go through (aliased port) */
293a9643ea8Slogwang {
294a9643ea8Slogwang 	int hlen, tlen, dlen, pflags;
295a9643ea8Slogwang 	char *sptr;
296a9643ea8Slogwang 	struct tcphdr *tc;
297a9643ea8Slogwang 
298a9643ea8Slogwang 	/* Calculate data length of TCP packet */
299a9643ea8Slogwang 	tc = (struct tcphdr *)ip_next(pip);
300a9643ea8Slogwang 	hlen = (pip->ip_hl + tc->th_off) << 2;
301a9643ea8Slogwang 	tlen = ntohs(pip->ip_len);
302a9643ea8Slogwang 	dlen = tlen - hlen;
303a9643ea8Slogwang 
304a9643ea8Slogwang 	/* Place string pointer and beginning of data */
305a9643ea8Slogwang 	sptr = (char *)pip;
306a9643ea8Slogwang 	sptr += hlen;
307a9643ea8Slogwang 
308a9643ea8Slogwang 	/*
309a9643ea8Slogwang 	 * Check that data length is not too long and previous message was
310a9643ea8Slogwang 	 * properly terminated with CRLF.
311a9643ea8Slogwang 	 */
312a9643ea8Slogwang 	pflags = GetProtocolFlags(lnk);
313a9643ea8Slogwang 	if (dlen <= MAX_MESSAGE_SIZE && (pflags & WAIT_CRLF) == 0 &&
314a9643ea8Slogwang 	    ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER &&
315a9643ea8Slogwang 	    (ParseFtpPortCommand(la, sptr, dlen) != 0 ||
316a9643ea8Slogwang 	     ParseFtpEprtCommand(la, sptr, dlen) != 0)) {
317a9643ea8Slogwang 		/*
318a9643ea8Slogwang 		 * Alias active mode client requesting data from server
319a9643ea8Slogwang 		 * behind NAT.  We need to alias server->client connection
320a9643ea8Slogwang 		 * to external address client is connecting to.
321a9643ea8Slogwang 		 */
322a9643ea8Slogwang 		AddLink(la, GetOriginalAddress(lnk), la->true_addr,
323a9643ea8Slogwang 		    GetAliasAddress(lnk), htons(FTP_CONTROL_PORT_NUMBER - 1),
324a9643ea8Slogwang 		    htons(la->true_port), GET_ALIAS_PORT, IPPROTO_TCP);
325a9643ea8Slogwang 	}
326a9643ea8Slogwang 	/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
327a9643ea8Slogwang 	if (dlen) {
328a9643ea8Slogwang 		sptr = (char *)pip;		/* start over at beginning */
329a9643ea8Slogwang 		tlen = ntohs(pip->ip_len);	/* recalc tlen, pkt may
330a9643ea8Slogwang 						 * have grown.
331a9643ea8Slogwang 						 */
332a9643ea8Slogwang 		if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
333a9643ea8Slogwang 			pflags &= ~WAIT_CRLF;
334a9643ea8Slogwang 		else
335a9643ea8Slogwang 			pflags |= WAIT_CRLF;
336a9643ea8Slogwang 		SetProtocolFlags(lnk, pflags);
337a9643ea8Slogwang        }
338a9643ea8Slogwang }
339a9643ea8Slogwang 
340a9643ea8Slogwang static int
ParseFtpPortCommand(struct libalias * la,char * sptr,int dlen)341a9643ea8Slogwang ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen)
342a9643ea8Slogwang {
343a9643ea8Slogwang 	char ch;
344a9643ea8Slogwang 	int i, state;
345a9643ea8Slogwang 	u_int32_t addr;
346a9643ea8Slogwang 	u_short port;
347a9643ea8Slogwang 	u_int8_t octet;
348a9643ea8Slogwang 
349a9643ea8Slogwang 	/* Format: "PORT A,D,D,R,PO,RT". */
350a9643ea8Slogwang 
351a9643ea8Slogwang 	/* Return if data length is too short. */
352a9643ea8Slogwang 	if (dlen < 18)
353a9643ea8Slogwang 		return (0);
354a9643ea8Slogwang 
355a9643ea8Slogwang 	if (strncasecmp("PORT ", sptr, 5))
356a9643ea8Slogwang 		return (0);
357a9643ea8Slogwang 
358a9643ea8Slogwang 	addr = port = octet = 0;
359a9643ea8Slogwang 	state = 0;
360a9643ea8Slogwang 	for (i = 5; i < dlen; i++) {
361a9643ea8Slogwang 		ch = sptr[i];
362a9643ea8Slogwang 		switch (state) {
363a9643ea8Slogwang 		case 0:
364a9643ea8Slogwang 			if (isspace(ch))
365a9643ea8Slogwang 				break;
366a9643ea8Slogwang 			else
367a9643ea8Slogwang 				state++;
368a9643ea8Slogwang 		case 1:
369a9643ea8Slogwang 		case 3:
370a9643ea8Slogwang 		case 5:
371a9643ea8Slogwang 		case 7:
372a9643ea8Slogwang 		case 9:
373a9643ea8Slogwang 		case 11:
374a9643ea8Slogwang 			if (isdigit(ch)) {
375a9643ea8Slogwang 				octet = ch - '0';
376a9643ea8Slogwang 				state++;
377a9643ea8Slogwang 			} else
378a9643ea8Slogwang 				return (0);
379a9643ea8Slogwang 			break;
380a9643ea8Slogwang 		case 2:
381a9643ea8Slogwang 		case 4:
382a9643ea8Slogwang 		case 6:
383a9643ea8Slogwang 		case 8:
384a9643ea8Slogwang 			if (isdigit(ch))
385a9643ea8Slogwang 				octet = 10 * octet + ch - '0';
386a9643ea8Slogwang 			else if (ch == ',') {
387a9643ea8Slogwang 				addr = (addr << 8) + octet;
388a9643ea8Slogwang 				state++;
389a9643ea8Slogwang 			} else
390a9643ea8Slogwang 				return (0);
391a9643ea8Slogwang 			break;
392a9643ea8Slogwang 		case 10:
393a9643ea8Slogwang 		case 12:
394a9643ea8Slogwang 			if (isdigit(ch))
395a9643ea8Slogwang 				octet = 10 * octet + ch - '0';
396a9643ea8Slogwang 			else if (ch == ',' || state == 12) {
397a9643ea8Slogwang 				port = (port << 8) + octet;
398a9643ea8Slogwang 				state++;
399a9643ea8Slogwang 			} else
400a9643ea8Slogwang 				return (0);
401a9643ea8Slogwang 			break;
402a9643ea8Slogwang 		}
403a9643ea8Slogwang 	}
404a9643ea8Slogwang 
405a9643ea8Slogwang 	if (state == 13) {
406a9643ea8Slogwang 		la->true_addr.s_addr = htonl(addr);
407a9643ea8Slogwang 		la->true_port = port;
408a9643ea8Slogwang 		return (1);
409a9643ea8Slogwang 	} else
410a9643ea8Slogwang 		return (0);
411a9643ea8Slogwang }
412a9643ea8Slogwang 
413a9643ea8Slogwang static int
ParseFtpEprtCommand(struct libalias * la,char * sptr,int dlen)414a9643ea8Slogwang ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen)
415a9643ea8Slogwang {
416a9643ea8Slogwang 	char ch, delim;
417a9643ea8Slogwang 	int i, state;
418a9643ea8Slogwang 	u_int32_t addr;
419a9643ea8Slogwang 	u_short port;
420a9643ea8Slogwang 	u_int8_t octet;
421a9643ea8Slogwang 
422a9643ea8Slogwang 	/* Format: "EPRT |1|A.D.D.R|PORT|". */
423a9643ea8Slogwang 
424a9643ea8Slogwang 	/* Return if data length is too short. */
425a9643ea8Slogwang 	if (dlen < 18)
426a9643ea8Slogwang 		return (0);
427a9643ea8Slogwang 
428a9643ea8Slogwang 	if (strncasecmp("EPRT ", sptr, 5))
429a9643ea8Slogwang 		return (0);
430a9643ea8Slogwang 
431a9643ea8Slogwang 	addr = port = octet = 0;
432a9643ea8Slogwang 	delim = '|';		/* XXX gcc -Wuninitialized */
433a9643ea8Slogwang 	state = 0;
434a9643ea8Slogwang 	for (i = 5; i < dlen; i++) {
435a9643ea8Slogwang 		ch = sptr[i];
436a9643ea8Slogwang 		switch (state) {
437a9643ea8Slogwang 		case 0:
438a9643ea8Slogwang 			if (!isspace(ch)) {
439a9643ea8Slogwang 				delim = ch;
440a9643ea8Slogwang 				state++;
441a9643ea8Slogwang 			}
442a9643ea8Slogwang 			break;
443a9643ea8Slogwang 		case 1:
444a9643ea8Slogwang 			if (ch == '1')	/* IPv4 address */
445a9643ea8Slogwang 				state++;
446a9643ea8Slogwang 			else
447a9643ea8Slogwang 				return (0);
448a9643ea8Slogwang 			break;
449a9643ea8Slogwang 		case 2:
450a9643ea8Slogwang 			if (ch == delim)
451a9643ea8Slogwang 				state++;
452a9643ea8Slogwang 			else
453a9643ea8Slogwang 				return (0);
454a9643ea8Slogwang 			break;
455a9643ea8Slogwang 		case 3:
456a9643ea8Slogwang 		case 5:
457a9643ea8Slogwang 		case 7:
458a9643ea8Slogwang 		case 9:
459a9643ea8Slogwang 			if (isdigit(ch)) {
460a9643ea8Slogwang 				octet = ch - '0';
461a9643ea8Slogwang 				state++;
462a9643ea8Slogwang 			} else
463a9643ea8Slogwang 				return (0);
464a9643ea8Slogwang 			break;
465a9643ea8Slogwang 		case 4:
466a9643ea8Slogwang 		case 6:
467a9643ea8Slogwang 		case 8:
468a9643ea8Slogwang 		case 10:
469a9643ea8Slogwang 			if (isdigit(ch))
470a9643ea8Slogwang 				octet = 10 * octet + ch - '0';
471a9643ea8Slogwang 			else if (ch == '.' || state == 10) {
472a9643ea8Slogwang 				addr = (addr << 8) + octet;
473a9643ea8Slogwang 				state++;
474a9643ea8Slogwang 			} else
475a9643ea8Slogwang 				return (0);
476a9643ea8Slogwang 			break;
477a9643ea8Slogwang 		case 11:
478a9643ea8Slogwang 			if (isdigit(ch)) {
479a9643ea8Slogwang 				port = ch - '0';
480a9643ea8Slogwang 				state++;
481a9643ea8Slogwang 			} else
482a9643ea8Slogwang 				return (0);
483a9643ea8Slogwang 			break;
484a9643ea8Slogwang 		case 12:
485a9643ea8Slogwang 			if (isdigit(ch))
486a9643ea8Slogwang 				port = 10 * port + ch - '0';
487a9643ea8Slogwang 			else if (ch == delim)
488a9643ea8Slogwang 				state++;
489a9643ea8Slogwang 			else
490a9643ea8Slogwang 				return (0);
491a9643ea8Slogwang 			break;
492a9643ea8Slogwang 		}
493a9643ea8Slogwang 	}
494a9643ea8Slogwang 
495a9643ea8Slogwang 	if (state == 13) {
496a9643ea8Slogwang 		la->true_addr.s_addr = htonl(addr);
497a9643ea8Slogwang 		la->true_port = port;
498a9643ea8Slogwang 		return (1);
499a9643ea8Slogwang 	} else
500a9643ea8Slogwang 		return (0);
501a9643ea8Slogwang }
502a9643ea8Slogwang 
503a9643ea8Slogwang static int
ParseFtp227Reply(struct libalias * la,char * sptr,int dlen)504a9643ea8Slogwang ParseFtp227Reply(struct libalias *la, char *sptr, int dlen)
505a9643ea8Slogwang {
506a9643ea8Slogwang 	char ch;
507a9643ea8Slogwang 	int i, state;
508a9643ea8Slogwang 	u_int32_t addr;
509a9643ea8Slogwang 	u_short port;
510a9643ea8Slogwang 	u_int8_t octet;
511a9643ea8Slogwang 
512a9643ea8Slogwang 	/* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
513a9643ea8Slogwang 
514a9643ea8Slogwang 	/* Return if data length is too short. */
515a9643ea8Slogwang 	if (dlen < 17)
516a9643ea8Slogwang 		return (0);
517a9643ea8Slogwang 
518a9643ea8Slogwang 	if (strncmp("227 ", sptr, 4))
519a9643ea8Slogwang 		return (0);
520a9643ea8Slogwang 
521a9643ea8Slogwang 	addr = port = octet = 0;
522a9643ea8Slogwang 
523a9643ea8Slogwang 	state = 0;
524a9643ea8Slogwang 	for (i = 4; i < dlen; i++) {
525a9643ea8Slogwang 		ch = sptr[i];
526a9643ea8Slogwang 		switch (state) {
527a9643ea8Slogwang 		case 0:
528a9643ea8Slogwang 			if (ch == '(')
529a9643ea8Slogwang 				state++;
530a9643ea8Slogwang 			break;
531a9643ea8Slogwang 		case 1:
532a9643ea8Slogwang 		case 3:
533a9643ea8Slogwang 		case 5:
534a9643ea8Slogwang 		case 7:
535a9643ea8Slogwang 		case 9:
536a9643ea8Slogwang 		case 11:
537a9643ea8Slogwang 			if (isdigit(ch)) {
538a9643ea8Slogwang 				octet = ch - '0';
539a9643ea8Slogwang 				state++;
540a9643ea8Slogwang 			} else
541a9643ea8Slogwang 				return (0);
542a9643ea8Slogwang 			break;
543a9643ea8Slogwang 		case 2:
544a9643ea8Slogwang 		case 4:
545a9643ea8Slogwang 		case 6:
546a9643ea8Slogwang 		case 8:
547a9643ea8Slogwang 			if (isdigit(ch))
548a9643ea8Slogwang 				octet = 10 * octet + ch - '0';
549a9643ea8Slogwang 			else if (ch == ',') {
550a9643ea8Slogwang 				addr = (addr << 8) + octet;
551a9643ea8Slogwang 				state++;
552a9643ea8Slogwang 			} else
553a9643ea8Slogwang 				return (0);
554a9643ea8Slogwang 			break;
555a9643ea8Slogwang 		case 10:
556a9643ea8Slogwang 		case 12:
557a9643ea8Slogwang 			if (isdigit(ch))
558a9643ea8Slogwang 				octet = 10 * octet + ch - '0';
559a9643ea8Slogwang 			else if (ch == ',' || (state == 12 && ch == ')')) {
560a9643ea8Slogwang 				port = (port << 8) + octet;
561a9643ea8Slogwang 				state++;
562a9643ea8Slogwang 			} else
563a9643ea8Slogwang 				return (0);
564a9643ea8Slogwang 			break;
565a9643ea8Slogwang 		}
566a9643ea8Slogwang 	}
567a9643ea8Slogwang 
568a9643ea8Slogwang 	if (state == 13) {
569a9643ea8Slogwang 		la->true_port = port;
570a9643ea8Slogwang 		la->true_addr.s_addr = htonl(addr);
571a9643ea8Slogwang 		return (1);
572a9643ea8Slogwang 	} else
573a9643ea8Slogwang 		return (0);
574a9643ea8Slogwang }
575a9643ea8Slogwang 
576a9643ea8Slogwang static int
ParseFtp229Reply(struct libalias * la,char * sptr,int dlen)577a9643ea8Slogwang ParseFtp229Reply(struct libalias *la, char *sptr, int dlen)
578a9643ea8Slogwang {
579a9643ea8Slogwang 	char ch, delim;
580a9643ea8Slogwang 	int i, state;
581a9643ea8Slogwang 	u_short port;
582a9643ea8Slogwang 
583a9643ea8Slogwang 	/* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
584a9643ea8Slogwang 
585a9643ea8Slogwang 	/* Return if data length is too short. */
586a9643ea8Slogwang 	if (dlen < 11)
587a9643ea8Slogwang 		return (0);
588a9643ea8Slogwang 
589a9643ea8Slogwang 	if (strncmp("229 ", sptr, 4))
590a9643ea8Slogwang 		return (0);
591a9643ea8Slogwang 
592a9643ea8Slogwang 	port = 0;
593a9643ea8Slogwang 	delim = '|';		/* XXX gcc -Wuninitialized */
594a9643ea8Slogwang 
595a9643ea8Slogwang 	state = 0;
596a9643ea8Slogwang 	for (i = 4; i < dlen; i++) {
597a9643ea8Slogwang 		ch = sptr[i];
598a9643ea8Slogwang 		switch (state) {
599a9643ea8Slogwang 		case 0:
600a9643ea8Slogwang 			if (ch == '(')
601a9643ea8Slogwang 				state++;
602a9643ea8Slogwang 			break;
603a9643ea8Slogwang 		case 1:
604a9643ea8Slogwang 			delim = ch;
605a9643ea8Slogwang 			state++;
606a9643ea8Slogwang 			break;
607a9643ea8Slogwang 		case 2:
608a9643ea8Slogwang 		case 3:
609a9643ea8Slogwang 			if (ch == delim)
610a9643ea8Slogwang 				state++;
611a9643ea8Slogwang 			else
612a9643ea8Slogwang 				return (0);
613a9643ea8Slogwang 			break;
614a9643ea8Slogwang 		case 4:
615a9643ea8Slogwang 			if (isdigit(ch)) {
616a9643ea8Slogwang 				port = ch - '0';
617a9643ea8Slogwang 				state++;
618a9643ea8Slogwang 			} else
619a9643ea8Slogwang 				return (0);
620a9643ea8Slogwang 			break;
621a9643ea8Slogwang 		case 5:
622a9643ea8Slogwang 			if (isdigit(ch))
623a9643ea8Slogwang 				port = 10 * port + ch - '0';
624a9643ea8Slogwang 			else if (ch == delim)
625a9643ea8Slogwang 				state++;
626a9643ea8Slogwang 			else
627a9643ea8Slogwang 				return (0);
628a9643ea8Slogwang 			break;
629a9643ea8Slogwang 		case 6:
630a9643ea8Slogwang 			if (ch == ')')
631a9643ea8Slogwang 				state++;
632a9643ea8Slogwang 			else
633a9643ea8Slogwang 				return (0);
634a9643ea8Slogwang 			break;
635a9643ea8Slogwang 		}
636a9643ea8Slogwang 	}
637a9643ea8Slogwang 
638a9643ea8Slogwang 	if (state == 7) {
639a9643ea8Slogwang 		la->true_port = port;
640a9643ea8Slogwang 		return (1);
641a9643ea8Slogwang 	} else
642a9643ea8Slogwang 		return (0);
643a9643ea8Slogwang }
644a9643ea8Slogwang 
645a9643ea8Slogwang static void
NewFtpMessage(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize,int ftp_message_type)646a9643ea8Slogwang NewFtpMessage(struct libalias *la, struct ip *pip,
647a9643ea8Slogwang     struct alias_link *lnk,
648a9643ea8Slogwang     int maxpacketsize,
649a9643ea8Slogwang     int ftp_message_type)
650a9643ea8Slogwang {
651a9643ea8Slogwang 	struct alias_link *ftp_lnk;
652a9643ea8Slogwang 
653a9643ea8Slogwang /* Security checks. */
654a9643ea8Slogwang 	if (pip->ip_src.s_addr != la->true_addr.s_addr)
655a9643ea8Slogwang 		return;
656a9643ea8Slogwang 
657a9643ea8Slogwang 	if (la->true_port < IPPORT_RESERVED)
658a9643ea8Slogwang 		return;
659a9643ea8Slogwang 
660a9643ea8Slogwang 	/* Establish link to address and port found in FTP control message. */
661a9643ea8Slogwang 	ftp_lnk = AddLink(la, la->true_addr, GetDestAddress(lnk),
662a9643ea8Slogwang 	    GetAliasAddress(lnk), htons(la->true_port), 0, GET_ALIAS_PORT,
663a9643ea8Slogwang 	    IPPROTO_TCP);
664a9643ea8Slogwang 
665a9643ea8Slogwang 	if (ftp_lnk != NULL) {
666a9643ea8Slogwang 		int slen, hlen, tlen, dlen;
667a9643ea8Slogwang 		struct tcphdr *tc;
668a9643ea8Slogwang 
669a9643ea8Slogwang #ifndef NO_FW_PUNCH
670a9643ea8Slogwang 		/* Punch hole in firewall */
671a9643ea8Slogwang 		PunchFWHole(ftp_lnk);
672a9643ea8Slogwang #endif
673a9643ea8Slogwang 
674a9643ea8Slogwang /* Calculate data length of TCP packet */
675a9643ea8Slogwang 		tc = (struct tcphdr *)ip_next(pip);
676a9643ea8Slogwang 		hlen = (pip->ip_hl + tc->th_off) << 2;
677a9643ea8Slogwang 		tlen = ntohs(pip->ip_len);
678a9643ea8Slogwang 		dlen = tlen - hlen;
679a9643ea8Slogwang 
680a9643ea8Slogwang /* Create new FTP message. */
681a9643ea8Slogwang 		{
682a9643ea8Slogwang 			char stemp[MAX_MESSAGE_SIZE + 1];
683a9643ea8Slogwang 			char *sptr;
684a9643ea8Slogwang 			u_short alias_port;
685a9643ea8Slogwang 			u_char *ptr;
686a9643ea8Slogwang 			int a1, a2, a3, a4, p1, p2;
687a9643ea8Slogwang 			struct in_addr alias_address;
688a9643ea8Slogwang 
689a9643ea8Slogwang /* Decompose alias address into quad format */
690a9643ea8Slogwang 			alias_address = GetAliasAddress(lnk);
691a9643ea8Slogwang 			ptr = (u_char *) & alias_address.s_addr;
692a9643ea8Slogwang 			a1 = *ptr++;
693a9643ea8Slogwang 			a2 = *ptr++;
694a9643ea8Slogwang 			a3 = *ptr++;
695a9643ea8Slogwang 			a4 = *ptr;
696a9643ea8Slogwang 
697a9643ea8Slogwang 			alias_port = GetAliasPort(ftp_lnk);
698a9643ea8Slogwang 
699a9643ea8Slogwang /* Prepare new command */
700a9643ea8Slogwang 			switch (ftp_message_type) {
701a9643ea8Slogwang 			case FTP_PORT_COMMAND:
702a9643ea8Slogwang 			case FTP_227_REPLY:
703a9643ea8Slogwang 				/* Decompose alias port into pair format. */
704a9643ea8Slogwang 				ptr = (char *)&alias_port;
705a9643ea8Slogwang 				p1 = *ptr++;
706a9643ea8Slogwang 				p2 = *ptr;
707a9643ea8Slogwang 
708a9643ea8Slogwang 				if (ftp_message_type == FTP_PORT_COMMAND) {
709a9643ea8Slogwang 					/* Generate PORT command string. */
710a9643ea8Slogwang 					sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
711a9643ea8Slogwang 					    a1, a2, a3, a4, p1, p2);
712a9643ea8Slogwang 				} else {
713a9643ea8Slogwang 					/* Generate 227 reply string. */
714a9643ea8Slogwang 					sprintf(stemp,
715a9643ea8Slogwang 					    "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
716a9643ea8Slogwang 					    a1, a2, a3, a4, p1, p2);
717a9643ea8Slogwang 				}
718a9643ea8Slogwang 				break;
719a9643ea8Slogwang 			case FTP_EPRT_COMMAND:
720a9643ea8Slogwang 				/* Generate EPRT command string. */
721a9643ea8Slogwang 				sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
722a9643ea8Slogwang 				    a1, a2, a3, a4, ntohs(alias_port));
723a9643ea8Slogwang 				break;
724a9643ea8Slogwang 			case FTP_229_REPLY:
725a9643ea8Slogwang 				/* Generate 229 reply string. */
726a9643ea8Slogwang 				sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
727a9643ea8Slogwang 				    ntohs(alias_port));
728a9643ea8Slogwang 				break;
729a9643ea8Slogwang 			}
730a9643ea8Slogwang 
731a9643ea8Slogwang /* Save string length for IP header modification */
732a9643ea8Slogwang 			slen = strlen(stemp);
733a9643ea8Slogwang 
734a9643ea8Slogwang /* Copy modified buffer into IP packet. */
735a9643ea8Slogwang 			sptr = (char *)pip;
736a9643ea8Slogwang 			sptr += hlen;
737a9643ea8Slogwang 			strncpy(sptr, stemp, maxpacketsize - hlen);
738a9643ea8Slogwang 		}
739a9643ea8Slogwang 
740a9643ea8Slogwang /* Save information regarding modified seq and ack numbers */
741a9643ea8Slogwang 		{
742a9643ea8Slogwang 			int delta;
743a9643ea8Slogwang 
744a9643ea8Slogwang 			SetAckModified(lnk);
745a9643ea8Slogwang 			tc = (struct tcphdr *)ip_next(pip);
746a9643ea8Slogwang 			delta = GetDeltaSeqOut(tc->th_seq, lnk);
747a9643ea8Slogwang 			AddSeq(lnk, delta + slen - dlen, pip->ip_hl,
748a9643ea8Slogwang 			    pip->ip_len, tc->th_seq, tc->th_off);
749a9643ea8Slogwang 		}
750a9643ea8Slogwang 
751a9643ea8Slogwang /* Revise IP header */
752a9643ea8Slogwang 		{
753a9643ea8Slogwang 			u_short new_len;
754a9643ea8Slogwang 
755*22ce4affSfengbojiang 			new_len = htons(hlen +
756*22ce4affSfengbojiang 			    MIN(slen, maxpacketsize - hlen));
757a9643ea8Slogwang 			DifferentialChecksum(&pip->ip_sum,
758a9643ea8Slogwang 			    &new_len,
759a9643ea8Slogwang 			    &pip->ip_len,
760a9643ea8Slogwang 			    1);
761a9643ea8Slogwang 			pip->ip_len = new_len;
762a9643ea8Slogwang 		}
763a9643ea8Slogwang 
764a9643ea8Slogwang /* Compute TCP checksum for revised packet */
765a9643ea8Slogwang 		tc->th_sum = 0;
766a9643ea8Slogwang #ifdef _KERNEL
767a9643ea8Slogwang 		tc->th_x2 = 1;
768a9643ea8Slogwang #else
769a9643ea8Slogwang 		tc->th_sum = TcpChecksum(pip);
770a9643ea8Slogwang #endif
771a9643ea8Slogwang 	} else {
772a9643ea8Slogwang #ifdef LIBALIAS_DEBUG
773a9643ea8Slogwang 		fprintf(stderr,
774a9643ea8Slogwang 		    "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
775a9643ea8Slogwang #endif
776a9643ea8Slogwang 	}
777a9643ea8Slogwang }
778