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