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