xref: /dflybsd-src/lib/libalias/alias_ftp.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*-
2*86d7f5d3SJohn Marino  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3*86d7f5d3SJohn Marino  * All rights reserved.
4*86d7f5d3SJohn Marino  *
5*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
6*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
7*86d7f5d3SJohn Marino  * are met:
8*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
9*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
10*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
11*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
12*86d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
13*86d7f5d3SJohn Marino  *
14*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*86d7f5d3SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*86d7f5d3SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*86d7f5d3SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*86d7f5d3SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*86d7f5d3SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*86d7f5d3SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*86d7f5d3SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*86d7f5d3SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*86d7f5d3SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*86d7f5d3SJohn Marino  * SUCH DAMAGE.
25*86d7f5d3SJohn Marino  *
26*86d7f5d3SJohn Marino  * $FreeBSD: src/lib/libalias/alias_ftp.c,v 1.5.2.7 2001/12/06 09:00:26 ru Exp $
27*86d7f5d3SJohn Marino  * $DragonFly: src/lib/libalias/alias_ftp.c,v 1.3 2004/08/20 00:08:17 joerg Exp $
28*86d7f5d3SJohn Marino  */
29*86d7f5d3SJohn Marino 
30*86d7f5d3SJohn Marino /*
31*86d7f5d3SJohn Marino     Alias_ftp.c performs special processing for FTP sessions under
32*86d7f5d3SJohn Marino     TCP.  Specifically, when a PORT/EPRT command from the client
33*86d7f5d3SJohn Marino     side or 227/229 reply from the server is sent, it is intercepted
34*86d7f5d3SJohn Marino     and modified.  The address is changed to the gateway machine
35*86d7f5d3SJohn Marino     and an aliasing port is used.
36*86d7f5d3SJohn Marino 
37*86d7f5d3SJohn Marino     For this routine to work, the message must fit entirely into a
38*86d7f5d3SJohn Marino     single TCP packet.  This is typically the case, but exceptions
39*86d7f5d3SJohn Marino     can easily be envisioned under the actual specifications.
40*86d7f5d3SJohn Marino 
41*86d7f5d3SJohn Marino     Probably the most troubling aspect of the approach taken here is
42*86d7f5d3SJohn Marino     that the new message will typically be a different length, and
43*86d7f5d3SJohn Marino     this causes a certain amount of bookkeeping to keep track of the
44*86d7f5d3SJohn Marino     changes of sequence and acknowledgment numbers, since the client
45*86d7f5d3SJohn Marino     machine is totally unaware of the modification to the TCP stream.
46*86d7f5d3SJohn Marino 
47*86d7f5d3SJohn Marino 
48*86d7f5d3SJohn Marino     References: RFC 959, RFC 2428.
49*86d7f5d3SJohn Marino 
50*86d7f5d3SJohn Marino     Initial version:  August, 1996  (cjm)
51*86d7f5d3SJohn Marino 
52*86d7f5d3SJohn Marino     Version 1.6
53*86d7f5d3SJohn Marino          Brian Somers and Martin Renters identified an IP checksum
54*86d7f5d3SJohn Marino          error for modified IP packets.
55*86d7f5d3SJohn Marino 
56*86d7f5d3SJohn Marino     Version 1.7:  January 9, 1996 (cjm)
57*86d7f5d3SJohn Marino          Differential checksum computation for change
58*86d7f5d3SJohn Marino          in IP packet length.
59*86d7f5d3SJohn Marino 
60*86d7f5d3SJohn Marino     Version 2.1:  May, 1997 (cjm)
61*86d7f5d3SJohn Marino          Very minor changes to conform with
62*86d7f5d3SJohn Marino          local/global/function naming conventions
63*86d7f5d3SJohn Marino          within the packet aliasing module.
64*86d7f5d3SJohn Marino 
65*86d7f5d3SJohn Marino     Version 3.1:  May, 2000 (eds)
66*86d7f5d3SJohn Marino 	 Add support for passive mode, alias the 227 replies.
67*86d7f5d3SJohn Marino 
68*86d7f5d3SJohn Marino     See HISTORY file for record of revisions.
69*86d7f5d3SJohn Marino */
70*86d7f5d3SJohn Marino 
71*86d7f5d3SJohn Marino /* Includes */
72*86d7f5d3SJohn Marino #include <sys/param.h>
73*86d7f5d3SJohn Marino #include <ctype.h>
74*86d7f5d3SJohn Marino #include <stdio.h>
75*86d7f5d3SJohn Marino #include <string.h>
76*86d7f5d3SJohn Marino #include <netinet/in_systm.h>
77*86d7f5d3SJohn Marino #include <netinet/in.h>
78*86d7f5d3SJohn Marino #include <netinet/ip.h>
79*86d7f5d3SJohn Marino #include <netinet/tcp.h>
80*86d7f5d3SJohn Marino 
81*86d7f5d3SJohn Marino #include "alias_local.h"
82*86d7f5d3SJohn Marino 
83*86d7f5d3SJohn Marino #define FTP_CONTROL_PORT_NUMBER 21
84*86d7f5d3SJohn Marino #define MAX_MESSAGE_SIZE	128
85*86d7f5d3SJohn Marino 
86*86d7f5d3SJohn Marino enum ftp_message_type {
87*86d7f5d3SJohn Marino     FTP_PORT_COMMAND,
88*86d7f5d3SJohn Marino     FTP_EPRT_COMMAND,
89*86d7f5d3SJohn Marino     FTP_227_REPLY,
90*86d7f5d3SJohn Marino     FTP_229_REPLY,
91*86d7f5d3SJohn Marino     FTP_UNKNOWN_MESSAGE
92*86d7f5d3SJohn Marino };
93*86d7f5d3SJohn Marino 
94*86d7f5d3SJohn Marino static int ParseFtpPortCommand(char *, int);
95*86d7f5d3SJohn Marino static int ParseFtpEprtCommand(char *, int);
96*86d7f5d3SJohn Marino static int ParseFtp227Reply(char *, int);
97*86d7f5d3SJohn Marino static int ParseFtp229Reply(char *, int);
98*86d7f5d3SJohn Marino static void NewFtpMessage(struct ip *, struct alias_link *, int, int);
99*86d7f5d3SJohn Marino 
100*86d7f5d3SJohn Marino static struct in_addr true_addr;	/* in network byte order. */
101*86d7f5d3SJohn Marino static u_short true_port;		/* in host byte order. */
102*86d7f5d3SJohn Marino 
103*86d7f5d3SJohn Marino void
AliasHandleFtpOut(struct ip * pip,struct alias_link * link,int maxpacketsize)104*86d7f5d3SJohn Marino AliasHandleFtpOut(
105*86d7f5d3SJohn Marino struct ip *pip,	  /* IP packet to examine/patch */
106*86d7f5d3SJohn Marino struct alias_link *link, /* The link to go through (aliased port) */
107*86d7f5d3SJohn Marino int maxpacketsize  /* The maximum size this packet can grow to (including headers) */)
108*86d7f5d3SJohn Marino {
109*86d7f5d3SJohn Marino     int hlen, tlen, dlen;
110*86d7f5d3SJohn Marino     char *sptr;
111*86d7f5d3SJohn Marino     struct tcphdr *tc;
112*86d7f5d3SJohn Marino     int ftp_message_type;
113*86d7f5d3SJohn Marino 
114*86d7f5d3SJohn Marino /* Calculate data length of TCP packet */
115*86d7f5d3SJohn Marino     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
116*86d7f5d3SJohn Marino     hlen = (pip->ip_hl + tc->th_off) << 2;
117*86d7f5d3SJohn Marino     tlen = ntohs(pip->ip_len);
118*86d7f5d3SJohn Marino     dlen = tlen - hlen;
119*86d7f5d3SJohn Marino 
120*86d7f5d3SJohn Marino /* Place string pointer and beginning of data */
121*86d7f5d3SJohn Marino     sptr = (char *) pip;
122*86d7f5d3SJohn Marino     sptr += hlen;
123*86d7f5d3SJohn Marino 
124*86d7f5d3SJohn Marino /*
125*86d7f5d3SJohn Marino  * Check that data length is not too long and previous message was
126*86d7f5d3SJohn Marino  * properly terminated with CRLF.
127*86d7f5d3SJohn Marino  */
128*86d7f5d3SJohn Marino     if (dlen <= MAX_MESSAGE_SIZE && GetLastLineCrlfTermed(link)) {
129*86d7f5d3SJohn Marino 	ftp_message_type = FTP_UNKNOWN_MESSAGE;
130*86d7f5d3SJohn Marino 
131*86d7f5d3SJohn Marino 	if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
132*86d7f5d3SJohn Marino /*
133*86d7f5d3SJohn Marino  * When aliasing a client, check for the PORT/EPRT command.
134*86d7f5d3SJohn Marino  */
135*86d7f5d3SJohn Marino 	    if (ParseFtpPortCommand(sptr, dlen))
136*86d7f5d3SJohn Marino 		ftp_message_type = FTP_PORT_COMMAND;
137*86d7f5d3SJohn Marino 	    else if (ParseFtpEprtCommand(sptr, dlen))
138*86d7f5d3SJohn Marino 		ftp_message_type = FTP_EPRT_COMMAND;
139*86d7f5d3SJohn Marino 	} else {
140*86d7f5d3SJohn Marino /*
141*86d7f5d3SJohn Marino  * When aliasing a server, check for the 227/229 reply.
142*86d7f5d3SJohn Marino  */
143*86d7f5d3SJohn Marino 	    if (ParseFtp227Reply(sptr, dlen))
144*86d7f5d3SJohn Marino 		ftp_message_type = FTP_227_REPLY;
145*86d7f5d3SJohn Marino 	    else if (ParseFtp229Reply(sptr, dlen)) {
146*86d7f5d3SJohn Marino 		ftp_message_type = FTP_229_REPLY;
147*86d7f5d3SJohn Marino 		true_addr.s_addr = pip->ip_src.s_addr;
148*86d7f5d3SJohn Marino 	    }
149*86d7f5d3SJohn Marino 	}
150*86d7f5d3SJohn Marino 
151*86d7f5d3SJohn Marino 	if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
152*86d7f5d3SJohn Marino 	    NewFtpMessage(pip, link, maxpacketsize, ftp_message_type);
153*86d7f5d3SJohn Marino     }
154*86d7f5d3SJohn Marino 
155*86d7f5d3SJohn Marino /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
156*86d7f5d3SJohn Marino 
157*86d7f5d3SJohn Marino     if (dlen) {                  /* only if there's data */
158*86d7f5d3SJohn Marino       sptr = (char *) pip; 	 /* start over at beginning */
159*86d7f5d3SJohn Marino       tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may have grown */
160*86d7f5d3SJohn Marino       SetLastLineCrlfTermed(link,
161*86d7f5d3SJohn Marino 			    (sptr[tlen-2] == '\r') && (sptr[tlen-1] == '\n'));
162*86d7f5d3SJohn Marino     }
163*86d7f5d3SJohn Marino }
164*86d7f5d3SJohn Marino 
165*86d7f5d3SJohn Marino static int
ParseFtpPortCommand(char * sptr,int dlen)166*86d7f5d3SJohn Marino ParseFtpPortCommand(char *sptr, int dlen)
167*86d7f5d3SJohn Marino {
168*86d7f5d3SJohn Marino     char ch;
169*86d7f5d3SJohn Marino     int i, state;
170*86d7f5d3SJohn Marino     u_int32_t addr;
171*86d7f5d3SJohn Marino     u_short port;
172*86d7f5d3SJohn Marino     u_int8_t octet;
173*86d7f5d3SJohn Marino 
174*86d7f5d3SJohn Marino     /* Format: "PORT A,D,D,R,PO,RT". */
175*86d7f5d3SJohn Marino 
176*86d7f5d3SJohn Marino     /* Return if data length is too short. */
177*86d7f5d3SJohn Marino     if (dlen < 18)
178*86d7f5d3SJohn Marino 	return 0;
179*86d7f5d3SJohn Marino 
180*86d7f5d3SJohn Marino     addr = port = octet = 0;
181*86d7f5d3SJohn Marino     state = -4;
182*86d7f5d3SJohn Marino     for (i = 0; i < dlen; i++) {
183*86d7f5d3SJohn Marino 	ch = sptr[i];
184*86d7f5d3SJohn Marino 	switch (state) {
185*86d7f5d3SJohn Marino 	case -4: if (ch == 'P') state++; else return 0; break;
186*86d7f5d3SJohn Marino 	case -3: if (ch == 'O') state++; else return 0; break;
187*86d7f5d3SJohn Marino 	case -2: if (ch == 'R') state++; else return 0; break;
188*86d7f5d3SJohn Marino 	case -1: if (ch == 'T') state++; else return 0; break;
189*86d7f5d3SJohn Marino 
190*86d7f5d3SJohn Marino 	case 0:
191*86d7f5d3SJohn Marino 	    if (isspace(ch))
192*86d7f5d3SJohn Marino 		break;
193*86d7f5d3SJohn Marino 	    else
194*86d7f5d3SJohn Marino 		state++;
195*86d7f5d3SJohn Marino 	case 1: case 3: case 5: case 7: case 9: case 11:
196*86d7f5d3SJohn Marino 	    if (isdigit(ch)) {
197*86d7f5d3SJohn Marino 		octet = ch - '0';
198*86d7f5d3SJohn Marino 		state++;
199*86d7f5d3SJohn Marino 	    } else
200*86d7f5d3SJohn Marino 		return 0;
201*86d7f5d3SJohn Marino 	    break;
202*86d7f5d3SJohn Marino 	case 2: case 4: case 6: case 8:
203*86d7f5d3SJohn Marino 	    if (isdigit(ch))
204*86d7f5d3SJohn Marino 		octet = 10 * octet + ch - '0';
205*86d7f5d3SJohn Marino             else if (ch == ',') {
206*86d7f5d3SJohn Marino 		addr = (addr << 8) + octet;
207*86d7f5d3SJohn Marino 		state++;
208*86d7f5d3SJohn Marino 	    } else
209*86d7f5d3SJohn Marino 		return 0;
210*86d7f5d3SJohn Marino 	    break;
211*86d7f5d3SJohn Marino 	case 10: case 12:
212*86d7f5d3SJohn Marino 	    if (isdigit(ch))
213*86d7f5d3SJohn Marino 		octet = 10 * octet + ch - '0';
214*86d7f5d3SJohn Marino 	    else if (ch == ',' || state == 12) {
215*86d7f5d3SJohn Marino 		port = (port << 8) + octet;
216*86d7f5d3SJohn Marino 		state++;
217*86d7f5d3SJohn Marino 	    } else
218*86d7f5d3SJohn Marino 		return 0;
219*86d7f5d3SJohn Marino 	    break;
220*86d7f5d3SJohn Marino 	}
221*86d7f5d3SJohn Marino     }
222*86d7f5d3SJohn Marino 
223*86d7f5d3SJohn Marino     if (state == 13) {
224*86d7f5d3SJohn Marino 	true_addr.s_addr = htonl(addr);
225*86d7f5d3SJohn Marino 	true_port = port;
226*86d7f5d3SJohn Marino 	return 1;
227*86d7f5d3SJohn Marino     } else
228*86d7f5d3SJohn Marino 	return 0;
229*86d7f5d3SJohn Marino }
230*86d7f5d3SJohn Marino 
231*86d7f5d3SJohn Marino static int
ParseFtpEprtCommand(char * sptr,int dlen)232*86d7f5d3SJohn Marino ParseFtpEprtCommand(char *sptr, int dlen)
233*86d7f5d3SJohn Marino {
234*86d7f5d3SJohn Marino     char ch, delim;
235*86d7f5d3SJohn Marino     int i, state;
236*86d7f5d3SJohn Marino     u_int32_t addr;
237*86d7f5d3SJohn Marino     u_short port;
238*86d7f5d3SJohn Marino     u_int8_t octet;
239*86d7f5d3SJohn Marino 
240*86d7f5d3SJohn Marino     /* Format: "EPRT |1|A.D.D.R|PORT|". */
241*86d7f5d3SJohn Marino 
242*86d7f5d3SJohn Marino     /* Return if data length is too short. */
243*86d7f5d3SJohn Marino     if (dlen < 18)
244*86d7f5d3SJohn Marino 	return 0;
245*86d7f5d3SJohn Marino 
246*86d7f5d3SJohn Marino     addr = port = octet = 0;
247*86d7f5d3SJohn Marino     delim = '|';			/* XXX gcc -Wuninitialized */
248*86d7f5d3SJohn Marino     state = -4;
249*86d7f5d3SJohn Marino     for (i = 0; i < dlen; i++) {
250*86d7f5d3SJohn Marino 	ch = sptr[i];
251*86d7f5d3SJohn Marino 	switch (state)
252*86d7f5d3SJohn Marino 	{
253*86d7f5d3SJohn Marino 	case -4: if (ch == 'E') state++; else return 0; break;
254*86d7f5d3SJohn Marino 	case -3: if (ch == 'P') state++; else return 0; break;
255*86d7f5d3SJohn Marino 	case -2: if (ch == 'R') state++; else return 0; break;
256*86d7f5d3SJohn Marino 	case -1: if (ch == 'T') state++; else return 0; break;
257*86d7f5d3SJohn Marino 
258*86d7f5d3SJohn Marino 	case 0:
259*86d7f5d3SJohn Marino 	    if (!isspace(ch)) {
260*86d7f5d3SJohn Marino 		delim = ch;
261*86d7f5d3SJohn Marino 		state++;
262*86d7f5d3SJohn Marino 	    }
263*86d7f5d3SJohn Marino 	    break;
264*86d7f5d3SJohn Marino 	case 1:
265*86d7f5d3SJohn Marino 	    if (ch == '1')	/* IPv4 address */
266*86d7f5d3SJohn Marino 		state++;
267*86d7f5d3SJohn Marino 	    else
268*86d7f5d3SJohn Marino 		return 0;
269*86d7f5d3SJohn Marino 	    break;
270*86d7f5d3SJohn Marino 	case 2:
271*86d7f5d3SJohn Marino 	    if (ch == delim)
272*86d7f5d3SJohn Marino 		state++;
273*86d7f5d3SJohn Marino 	    else
274*86d7f5d3SJohn Marino 		return 0;
275*86d7f5d3SJohn Marino 	    break;
276*86d7f5d3SJohn Marino 	case 3: case 5: case 7: case 9:
277*86d7f5d3SJohn Marino 	    if (isdigit(ch)) {
278*86d7f5d3SJohn Marino 		octet = ch - '0';
279*86d7f5d3SJohn Marino 		state++;
280*86d7f5d3SJohn Marino 	    } else
281*86d7f5d3SJohn Marino 		return 0;
282*86d7f5d3SJohn Marino 	    break;
283*86d7f5d3SJohn Marino 	case 4: case 6: case 8: case 10:
284*86d7f5d3SJohn Marino 	    if (isdigit(ch))
285*86d7f5d3SJohn Marino 		octet = 10 * octet + ch - '0';
286*86d7f5d3SJohn Marino             else if (ch == '.' || state == 10) {
287*86d7f5d3SJohn Marino 		addr = (addr << 8) + octet;
288*86d7f5d3SJohn Marino 		state++;
289*86d7f5d3SJohn Marino 	    } else
290*86d7f5d3SJohn Marino 		return 0;
291*86d7f5d3SJohn Marino 	    break;
292*86d7f5d3SJohn Marino 	case 11:
293*86d7f5d3SJohn Marino 	    if (isdigit(ch)) {
294*86d7f5d3SJohn Marino 		port = ch - '0';
295*86d7f5d3SJohn Marino 		state++;
296*86d7f5d3SJohn Marino 	    } else
297*86d7f5d3SJohn Marino 		return 0;
298*86d7f5d3SJohn Marino 	    break;
299*86d7f5d3SJohn Marino 	case 12:
300*86d7f5d3SJohn Marino 	    if (isdigit(ch))
301*86d7f5d3SJohn Marino 		port = 10 * port + ch - '0';
302*86d7f5d3SJohn Marino 	    else if (ch == delim)
303*86d7f5d3SJohn Marino 		state++;
304*86d7f5d3SJohn Marino 	    else
305*86d7f5d3SJohn Marino 		return 0;
306*86d7f5d3SJohn Marino 	    break;
307*86d7f5d3SJohn Marino 	}
308*86d7f5d3SJohn Marino     }
309*86d7f5d3SJohn Marino 
310*86d7f5d3SJohn Marino     if (state == 13) {
311*86d7f5d3SJohn Marino 	true_addr.s_addr = htonl(addr);
312*86d7f5d3SJohn Marino 	true_port = port;
313*86d7f5d3SJohn Marino 	return 1;
314*86d7f5d3SJohn Marino     } else
315*86d7f5d3SJohn Marino 	return 0;
316*86d7f5d3SJohn Marino }
317*86d7f5d3SJohn Marino 
318*86d7f5d3SJohn Marino static int
ParseFtp227Reply(char * sptr,int dlen)319*86d7f5d3SJohn Marino ParseFtp227Reply(char *sptr, int dlen)
320*86d7f5d3SJohn Marino {
321*86d7f5d3SJohn Marino     char ch;
322*86d7f5d3SJohn Marino     int i, state;
323*86d7f5d3SJohn Marino     u_int32_t addr;
324*86d7f5d3SJohn Marino     u_short port;
325*86d7f5d3SJohn Marino     u_int8_t octet;
326*86d7f5d3SJohn Marino 
327*86d7f5d3SJohn Marino     /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
328*86d7f5d3SJohn Marino 
329*86d7f5d3SJohn Marino     /* Return if data length is too short. */
330*86d7f5d3SJohn Marino     if (dlen < 17)
331*86d7f5d3SJohn Marino 	return 0;
332*86d7f5d3SJohn Marino 
333*86d7f5d3SJohn Marino     addr = port = octet = 0;
334*86d7f5d3SJohn Marino 
335*86d7f5d3SJohn Marino     state = -3;
336*86d7f5d3SJohn Marino     for (i = 0; i < dlen; i++) {
337*86d7f5d3SJohn Marino         ch = sptr[i];
338*86d7f5d3SJohn Marino         switch (state)
339*86d7f5d3SJohn Marino         {
340*86d7f5d3SJohn Marino         case -3: if (ch == '2') state++; else return 0; break;
341*86d7f5d3SJohn Marino         case -2: if (ch == '2') state++; else return 0; break;
342*86d7f5d3SJohn Marino         case -1: if (ch == '7') state++; else return 0; break;
343*86d7f5d3SJohn Marino 
344*86d7f5d3SJohn Marino 	case 0:
345*86d7f5d3SJohn Marino 	    if (ch == '(')
346*86d7f5d3SJohn Marino 		state++;
347*86d7f5d3SJohn Marino 	    break;
348*86d7f5d3SJohn Marino 	case 1: case 3: case 5: case 7: case 9: case 11:
349*86d7f5d3SJohn Marino 	    if (isdigit(ch)) {
350*86d7f5d3SJohn Marino 		octet = ch - '0';
351*86d7f5d3SJohn Marino 		state++;
352*86d7f5d3SJohn Marino 	    } else
353*86d7f5d3SJohn Marino 		return 0;
354*86d7f5d3SJohn Marino 	    break;
355*86d7f5d3SJohn Marino 	case 2: case 4: case 6: case 8:
356*86d7f5d3SJohn Marino 	    if (isdigit(ch))
357*86d7f5d3SJohn Marino 		octet = 10 * octet + ch - '0';
358*86d7f5d3SJohn Marino             else if (ch == ',') {
359*86d7f5d3SJohn Marino 		addr = (addr << 8) + octet;
360*86d7f5d3SJohn Marino 		state++;
361*86d7f5d3SJohn Marino 	    } else
362*86d7f5d3SJohn Marino 		return 0;
363*86d7f5d3SJohn Marino 	    break;
364*86d7f5d3SJohn Marino 	case 10: case 12:
365*86d7f5d3SJohn Marino 	    if (isdigit(ch))
366*86d7f5d3SJohn Marino 		octet = 10 * octet + ch - '0';
367*86d7f5d3SJohn Marino 	    else if (ch == ',' || (state == 12 && ch == ')')) {
368*86d7f5d3SJohn Marino 		port = (port << 8) + octet;
369*86d7f5d3SJohn Marino 		state++;
370*86d7f5d3SJohn Marino 	    } else
371*86d7f5d3SJohn Marino 		return 0;
372*86d7f5d3SJohn Marino 	    break;
373*86d7f5d3SJohn Marino 	}
374*86d7f5d3SJohn Marino     }
375*86d7f5d3SJohn Marino 
376*86d7f5d3SJohn Marino     if (state == 13) {
377*86d7f5d3SJohn Marino         true_port = port;
378*86d7f5d3SJohn Marino         true_addr.s_addr = htonl(addr);
379*86d7f5d3SJohn Marino 	return 1;
380*86d7f5d3SJohn Marino     } else
381*86d7f5d3SJohn Marino 	return 0;
382*86d7f5d3SJohn Marino }
383*86d7f5d3SJohn Marino 
384*86d7f5d3SJohn Marino static int
ParseFtp229Reply(char * sptr,int dlen)385*86d7f5d3SJohn Marino ParseFtp229Reply(char *sptr, int dlen)
386*86d7f5d3SJohn Marino {
387*86d7f5d3SJohn Marino     char ch, delim;
388*86d7f5d3SJohn Marino     int i, state;
389*86d7f5d3SJohn Marino     u_short port;
390*86d7f5d3SJohn Marino 
391*86d7f5d3SJohn Marino     /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
392*86d7f5d3SJohn Marino 
393*86d7f5d3SJohn Marino     /* Return if data length is too short. */
394*86d7f5d3SJohn Marino     if (dlen < 11)
395*86d7f5d3SJohn Marino 	return 0;
396*86d7f5d3SJohn Marino 
397*86d7f5d3SJohn Marino     port = 0;
398*86d7f5d3SJohn Marino     delim = '|';			/* XXX gcc -Wuninitialized */
399*86d7f5d3SJohn Marino 
400*86d7f5d3SJohn Marino     state = -3;
401*86d7f5d3SJohn Marino     for (i = 0; i < dlen; i++) {
402*86d7f5d3SJohn Marino 	ch = sptr[i];
403*86d7f5d3SJohn Marino 	switch (state)
404*86d7f5d3SJohn Marino 	{
405*86d7f5d3SJohn Marino 	case -3: if (ch == '2') state++; else return 0; break;
406*86d7f5d3SJohn Marino 	case -2: if (ch == '2') state++; else return 0; break;
407*86d7f5d3SJohn Marino 	case -1: if (ch == '9') state++; else return 0; break;
408*86d7f5d3SJohn Marino 
409*86d7f5d3SJohn Marino 	case 0:
410*86d7f5d3SJohn Marino 	    if (ch == '(')
411*86d7f5d3SJohn Marino 		state++;
412*86d7f5d3SJohn Marino 	    break;
413*86d7f5d3SJohn Marino 	case 1:
414*86d7f5d3SJohn Marino 	    delim = ch;
415*86d7f5d3SJohn Marino 	    state++;
416*86d7f5d3SJohn Marino 	    break;
417*86d7f5d3SJohn Marino 	case 2: case 3:
418*86d7f5d3SJohn Marino 	    if (ch == delim)
419*86d7f5d3SJohn Marino 		state++;
420*86d7f5d3SJohn Marino 	    else
421*86d7f5d3SJohn Marino 		return 0;
422*86d7f5d3SJohn Marino 	    break;
423*86d7f5d3SJohn Marino 	case 4:
424*86d7f5d3SJohn Marino 	    if (isdigit(ch)) {
425*86d7f5d3SJohn Marino 		port = ch - '0';
426*86d7f5d3SJohn Marino 		state++;
427*86d7f5d3SJohn Marino 	    } else
428*86d7f5d3SJohn Marino 		return 0;
429*86d7f5d3SJohn Marino 	    break;
430*86d7f5d3SJohn Marino 	case 5:
431*86d7f5d3SJohn Marino 	    if (isdigit(ch))
432*86d7f5d3SJohn Marino 		port = 10 * port + ch - '0';
433*86d7f5d3SJohn Marino 	    else if (ch == delim)
434*86d7f5d3SJohn Marino 		state++;
435*86d7f5d3SJohn Marino 	    else
436*86d7f5d3SJohn Marino 		return 0;
437*86d7f5d3SJohn Marino 	    break;
438*86d7f5d3SJohn Marino 	case 6:
439*86d7f5d3SJohn Marino 	    if (ch == ')')
440*86d7f5d3SJohn Marino 		state++;
441*86d7f5d3SJohn Marino 	    else
442*86d7f5d3SJohn Marino 		return 0;
443*86d7f5d3SJohn Marino 	    break;
444*86d7f5d3SJohn Marino 	}
445*86d7f5d3SJohn Marino     }
446*86d7f5d3SJohn Marino 
447*86d7f5d3SJohn Marino     if (state == 7) {
448*86d7f5d3SJohn Marino 	true_port = port;
449*86d7f5d3SJohn Marino 	return 1;
450*86d7f5d3SJohn Marino     } else
451*86d7f5d3SJohn Marino 	return 0;
452*86d7f5d3SJohn Marino }
453*86d7f5d3SJohn Marino 
454*86d7f5d3SJohn Marino static void
NewFtpMessage(struct ip * pip,struct alias_link * link,int maxpacketsize,int ftp_message_type)455*86d7f5d3SJohn Marino NewFtpMessage(struct ip *pip,
456*86d7f5d3SJohn Marino               struct alias_link *link,
457*86d7f5d3SJohn Marino               int maxpacketsize,
458*86d7f5d3SJohn Marino               int ftp_message_type)
459*86d7f5d3SJohn Marino {
460*86d7f5d3SJohn Marino     struct alias_link *ftp_link;
461*86d7f5d3SJohn Marino 
462*86d7f5d3SJohn Marino /* Security checks. */
463*86d7f5d3SJohn Marino     if (pip->ip_src.s_addr != true_addr.s_addr)
464*86d7f5d3SJohn Marino 	return;
465*86d7f5d3SJohn Marino 
466*86d7f5d3SJohn Marino     if (true_port < IPPORT_RESERVED)
467*86d7f5d3SJohn Marino 	return;
468*86d7f5d3SJohn Marino 
469*86d7f5d3SJohn Marino /* Establish link to address and port found in FTP control message. */
470*86d7f5d3SJohn Marino     ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link),
471*86d7f5d3SJohn Marino                              htons(true_port), 0, IPPROTO_TCP, 1);
472*86d7f5d3SJohn Marino 
473*86d7f5d3SJohn Marino     if (ftp_link != NULL)
474*86d7f5d3SJohn Marino     {
475*86d7f5d3SJohn Marino         int slen, hlen, tlen, dlen;
476*86d7f5d3SJohn Marino         struct tcphdr *tc;
477*86d7f5d3SJohn Marino 
478*86d7f5d3SJohn Marino #ifndef NO_FW_PUNCH
479*86d7f5d3SJohn Marino 	/* Punch hole in firewall */
480*86d7f5d3SJohn Marino 	PunchFWHole(ftp_link);
481*86d7f5d3SJohn Marino #endif
482*86d7f5d3SJohn Marino 
483*86d7f5d3SJohn Marino /* Calculate data length of TCP packet */
484*86d7f5d3SJohn Marino         tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
485*86d7f5d3SJohn Marino         hlen = (pip->ip_hl + tc->th_off) << 2;
486*86d7f5d3SJohn Marino         tlen = ntohs(pip->ip_len);
487*86d7f5d3SJohn Marino         dlen = tlen - hlen;
488*86d7f5d3SJohn Marino 
489*86d7f5d3SJohn Marino /* Create new FTP message. */
490*86d7f5d3SJohn Marino         {
491*86d7f5d3SJohn Marino             char stemp[MAX_MESSAGE_SIZE + 1];
492*86d7f5d3SJohn Marino             char *sptr;
493*86d7f5d3SJohn Marino             u_short alias_port;
494*86d7f5d3SJohn Marino             u_char *ptr;
495*86d7f5d3SJohn Marino             int a1, a2, a3, a4, p1, p2;
496*86d7f5d3SJohn Marino             struct in_addr alias_address;
497*86d7f5d3SJohn Marino 
498*86d7f5d3SJohn Marino /* Decompose alias address into quad format */
499*86d7f5d3SJohn Marino             alias_address = GetAliasAddress(link);
500*86d7f5d3SJohn Marino             ptr = (u_char *) &alias_address.s_addr;
501*86d7f5d3SJohn Marino             a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr;
502*86d7f5d3SJohn Marino 
503*86d7f5d3SJohn Marino 	    alias_port = GetAliasPort(ftp_link);
504*86d7f5d3SJohn Marino 
505*86d7f5d3SJohn Marino 	    switch (ftp_message_type)
506*86d7f5d3SJohn Marino 	    {
507*86d7f5d3SJohn Marino 	    case FTP_PORT_COMMAND:
508*86d7f5d3SJohn Marino 	    case FTP_227_REPLY:
509*86d7f5d3SJohn Marino 		/* Decompose alias port into pair format. */
510*86d7f5d3SJohn Marino 		ptr = (char *) &alias_port;
511*86d7f5d3SJohn Marino 		p1 = *ptr++; p2=*ptr;
512*86d7f5d3SJohn Marino 
513*86d7f5d3SJohn Marino 		if (ftp_message_type == FTP_PORT_COMMAND) {
514*86d7f5d3SJohn Marino 		    /* Generate PORT command string. */
515*86d7f5d3SJohn Marino 		    sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
516*86d7f5d3SJohn Marino 			    a1,a2,a3,a4,p1,p2);
517*86d7f5d3SJohn Marino 		} else {
518*86d7f5d3SJohn Marino 		    /* Generate 227 reply string. */
519*86d7f5d3SJohn Marino 		    sprintf(stemp,
520*86d7f5d3SJohn Marino 			    "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
521*86d7f5d3SJohn Marino 			    a1,a2,a3,a4,p1,p2);
522*86d7f5d3SJohn Marino 		}
523*86d7f5d3SJohn Marino 		break;
524*86d7f5d3SJohn Marino 	    case FTP_EPRT_COMMAND:
525*86d7f5d3SJohn Marino 		/* Generate EPRT command string. */
526*86d7f5d3SJohn Marino 		sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
527*86d7f5d3SJohn Marino 			a1,a2,a3,a4,ntohs(alias_port));
528*86d7f5d3SJohn Marino 		break;
529*86d7f5d3SJohn Marino 	    case FTP_229_REPLY:
530*86d7f5d3SJohn Marino 		/* Generate 229 reply string. */
531*86d7f5d3SJohn Marino 		sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
532*86d7f5d3SJohn Marino 			ntohs(alias_port));
533*86d7f5d3SJohn Marino 		break;
534*86d7f5d3SJohn Marino 	    }
535*86d7f5d3SJohn Marino 
536*86d7f5d3SJohn Marino /* Save string length for IP header modification */
537*86d7f5d3SJohn Marino             slen = strlen(stemp);
538*86d7f5d3SJohn Marino 
539*86d7f5d3SJohn Marino /* Copy modified buffer into IP packet. */
540*86d7f5d3SJohn Marino             sptr = (char *) pip; sptr += hlen;
541*86d7f5d3SJohn Marino             strncpy(sptr, stemp, maxpacketsize-hlen);
542*86d7f5d3SJohn Marino         }
543*86d7f5d3SJohn Marino 
544*86d7f5d3SJohn Marino /* Save information regarding modified seq and ack numbers */
545*86d7f5d3SJohn Marino         {
546*86d7f5d3SJohn Marino             int delta;
547*86d7f5d3SJohn Marino 
548*86d7f5d3SJohn Marino             SetAckModified(link);
549*86d7f5d3SJohn Marino             delta = GetDeltaSeqOut(pip, link);
550*86d7f5d3SJohn Marino             AddSeq(pip, link, delta+slen-dlen);
551*86d7f5d3SJohn Marino         }
552*86d7f5d3SJohn Marino 
553*86d7f5d3SJohn Marino /* Revise IP header */
554*86d7f5d3SJohn Marino         {
555*86d7f5d3SJohn Marino             u_short new_len;
556*86d7f5d3SJohn Marino 
557*86d7f5d3SJohn Marino             new_len = htons(hlen + slen);
558*86d7f5d3SJohn Marino             DifferentialChecksum(&pip->ip_sum,
559*86d7f5d3SJohn Marino                                  &new_len,
560*86d7f5d3SJohn Marino                                  &pip->ip_len,
561*86d7f5d3SJohn Marino                                  1);
562*86d7f5d3SJohn Marino             pip->ip_len = new_len;
563*86d7f5d3SJohn Marino         }
564*86d7f5d3SJohn Marino 
565*86d7f5d3SJohn Marino /* Compute TCP checksum for revised packet */
566*86d7f5d3SJohn Marino         tc->th_sum = 0;
567*86d7f5d3SJohn Marino         tc->th_sum = TcpChecksum(pip);
568*86d7f5d3SJohn Marino     }
569*86d7f5d3SJohn Marino     else
570*86d7f5d3SJohn Marino     {
571*86d7f5d3SJohn Marino #ifdef DEBUG
572*86d7f5d3SJohn Marino         fprintf(stderr,
573*86d7f5d3SJohn Marino         "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
574*86d7f5d3SJohn Marino #endif
575*86d7f5d3SJohn Marino     }
576*86d7f5d3SJohn Marino }
577