]>
git.saurik.com Git - apple/network_cmds.git/blob - alias/alias_ftp.c
db53902d4b3a613d17bf9be2416d23b9b5fbd263
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
27 * All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
38 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
42 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * $FreeBSD: src/lib/libalias/alias_ftp.c,v 1.5.2.4 2001/08/21 03:50:25 brian Exp $
55 Alias_ftp.c performs special processing for FTP sessions under
56 TCP. Specifically, when a PORT/EPRT command from the client
57 side or 227/229 reply from the server is sent, it is intercepted
58 and modified. The address is changed to the gateway machine
59 and an aliasing port is used.
61 For this routine to work, the message must fit entirely into a
62 single TCP packet. This is typically the case, but exceptions
63 can easily be envisioned under the actual specifications.
65 Probably the most troubling aspect of the approach taken here is
66 that the new message will typically be a different length, and
67 this causes a certain amount of bookkeeping to keep track of the
68 changes of sequence and acknowledgment numbers, since the client
69 machine is totally unaware of the modification to the TCP stream.
72 References: RFC 959, RFC 2428.
74 Initial version: August, 1996 (cjm)
77 Brian Somers and Martin Renters identified an IP checksum
78 error for modified IP packets.
80 Version 1.7: January 9, 1996 (cjm)
81 Differential checksum computation for change
84 Version 2.1: May, 1997 (cjm)
85 Very minor changes to conform with
86 local/global/function naming conventions
87 within the packet aliasing module.
89 Version 3.1: May, 2000 (eds)
90 Add support for passive mode, alias the 227 replies.
92 See HISTORY file for record of revisions.
99 #include <sys/types.h>
100 #include <netinet/in_systm.h>
101 #include <netinet/in.h>
102 #include <netinet/ip.h>
103 #include <netinet/tcp.h>
105 #include "alias_local.h"
107 #define FTP_CONTROL_PORT_NUMBER 21
108 #define MAX_MESSAGE_SIZE 128
110 enum ftp_message_type
{
118 static int ParseFtpPortCommand(char *, int);
119 static int ParseFtpEprtCommand(char *, int);
120 static int ParseFtp227Reply(char *, int);
121 static int ParseFtp229Reply(char *, int);
122 static void NewFtpMessage(struct ip
*, struct alias_link
*, int, int);
124 static struct in_addr true_addr
; /* in network byte order. */
125 static u_short true_port
; /* in host byte order. */
129 struct ip
*pip
, /* IP packet to examine/patch */
130 struct alias_link
*link
, /* The link to go through (aliased port) */
131 int maxpacketsize
/* The maximum size this packet can grow to (including headers) */)
133 int hlen
, tlen
, dlen
;
136 int ftp_message_type
;
138 /* Calculate data length of TCP packet */
139 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
140 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
141 tlen
= ntohs(pip
->ip_len
);
144 /* Place string pointer and beginning of data */
149 * Check that data length is not too long and previous message was
150 * properly terminated with CRLF.
152 if (dlen
<= MAX_MESSAGE_SIZE
&& GetLastLineCrlfTermed(link
)) {
153 ftp_message_type
= FTP_UNKNOWN_MESSAGE
;
155 if (ntohs(tc
->th_dport
) == FTP_CONTROL_PORT_NUMBER
) {
157 * When aliasing a client, check for the PORT/EPRT command.
159 if (ParseFtpPortCommand(sptr
, dlen
))
160 ftp_message_type
= FTP_PORT_COMMAND
;
161 else if (ParseFtpEprtCommand(sptr
, dlen
))
162 ftp_message_type
= FTP_EPRT_COMMAND
;
165 * When aliasing a server, check for the 227/229 reply.
167 if (ParseFtp227Reply(sptr
, dlen
))
168 ftp_message_type
= FTP_227_REPLY
;
169 else if (ParseFtp229Reply(sptr
, dlen
))
170 ftp_message_type
= FTP_229_REPLY
;
173 if (ftp_message_type
!= FTP_UNKNOWN_MESSAGE
)
174 NewFtpMessage(pip
, link
, maxpacketsize
, ftp_message_type
);
177 /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
179 if (dlen
) { /* only if there's data */
180 sptr
= (char *) pip
; /* start over at beginning */
181 tlen
= ntohs(pip
->ip_len
); /* recalc tlen, pkt may have grown */
182 SetLastLineCrlfTermed(link
,
183 (sptr
[tlen
-2] == '\r') && (sptr
[tlen
-1] == '\n'));
188 ParseFtpPortCommand(char *sptr
, int dlen
)
196 /* Format: "PORT A,D,D,R,PO,RT". */
198 /* Return if data length is too short. */
202 addr
= port
= octet
= 0;
204 for (i
= 0; i
< dlen
; i
++) {
207 case -4: if (ch
== 'P') state
++; else return 0; break;
208 case -3: if (ch
== 'O') state
++; else return 0; break;
209 case -2: if (ch
== 'R') state
++; else return 0; break;
210 case -1: if (ch
== 'T') state
++; else return 0; break;
217 case 1: case 3: case 5: case 7: case 9: case 11:
224 case 2: case 4: case 6: case 8:
226 octet
= 10 * octet
+ ch
- '0';
227 else if (ch
== ',') {
228 addr
= (addr
<< 8) + octet
;
235 octet
= 10 * octet
+ ch
- '0';
236 else if (ch
== ',' || state
== 12) {
237 port
= (port
<< 8) + octet
;
246 true_addr
.s_addr
= htonl(addr
);
254 ParseFtpEprtCommand(char *sptr
, int dlen
)
262 /* Format: "EPRT |1|A.D.D.R|PORT|". */
264 /* Return if data length is too short. */
268 addr
= port
= octet
= 0;
269 delim
= '|'; /* XXX gcc -Wuninitialized */
271 for (i
= 0; i
< dlen
; i
++) {
275 case -4: if (ch
== 'E') state
++; else return 0; break;
276 case -3: if (ch
== 'P') state
++; else return 0; break;
277 case -2: if (ch
== 'R') state
++; else return 0; break;
278 case -1: if (ch
== 'T') state
++; else return 0; break;
287 if (ch
== '1') /* IPv4 address */
298 case 3: case 5: case 7: case 9:
305 case 4: case 6: case 8: case 10:
307 octet
= 10 * octet
+ ch
- '0';
308 else if (ch
== '.' || state
== 10) {
309 addr
= (addr
<< 8) + octet
;
323 port
= 10 * port
+ ch
- '0';
324 else if (ch
== delim
)
333 true_addr
.s_addr
= htonl(addr
);
341 ParseFtp227Reply(char *sptr
, int dlen
)
349 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
351 /* Return if data length is too short. */
355 addr
= port
= octet
= 0;
358 for (i
= 0; i
< dlen
; i
++) {
362 case -3: if (ch
== '2') state
++; else return 0; break;
363 case -2: if (ch
== '2') state
++; else return 0; break;
364 case -1: if (ch
== '7') state
++; else return 0; break;
370 case 1: case 3: case 5: case 7: case 9: case 11:
377 case 2: case 4: case 6: case 8:
379 octet
= 10 * octet
+ ch
- '0';
380 else if (ch
== ',') {
381 addr
= (addr
<< 8) + octet
;
388 octet
= 10 * octet
+ ch
- '0';
389 else if (ch
== ',' || (state
== 12 && ch
== ')')) {
390 port
= (port
<< 8) + octet
;
400 true_addr
.s_addr
= htonl(addr
);
407 ParseFtp229Reply(char *sptr
, int dlen
)
413 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
415 /* Return if data length is too short. */
420 delim
= '|'; /* XXX gcc -Wuninitialized */
423 for (i
= 0; i
< dlen
; i
++) {
427 case -3: if (ch
== '2') state
++; else return 0; break;
428 case -2: if (ch
== '2') state
++; else return 0; break;
429 case -1: if (ch
== '9') state
++; else return 0; break;
454 port
= 10 * port
+ ch
- '0';
455 else if (ch
== delim
)
477 NewFtpMessage(struct ip
*pip
,
478 struct alias_link
*link
,
480 int ftp_message_type
)
482 struct alias_link
*ftp_link
;
484 /* Security checks. */
485 if (ftp_message_type
!= FTP_229_REPLY
&&
486 pip
->ip_src
.s_addr
!= true_addr
.s_addr
)
489 if (true_port
< IPPORT_RESERVED
)
492 /* Establish link to address and port found in FTP control message. */
493 ftp_link
= FindUdpTcpOut(true_addr
, GetDestAddress(link
),
494 htons(true_port
), 0, IPPROTO_TCP
, 1);
496 if (ftp_link
!= NULL
)
498 int slen
, hlen
, tlen
, dlen
;
502 if (ftp_message_type
== FTP_PORT_COMMAND
||
503 ftp_message_type
== FTP_EPRT_COMMAND
) {
504 /* Punch hole in firewall */
505 PunchFWHole(ftp_link
);
509 /* Calculate data length of TCP packet */
510 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
511 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
512 tlen
= ntohs(pip
->ip_len
);
515 /* Create new FTP message. */
517 char stemp
[MAX_MESSAGE_SIZE
+ 1];
521 int a1
, a2
, a3
, a4
, p1
, p2
;
522 struct in_addr alias_address
;
524 /* Decompose alias address into quad format */
525 alias_address
= GetAliasAddress(link
);
526 ptr
= (u_char
*) &alias_address
.s_addr
;
527 a1
= *ptr
++; a2
=*ptr
++; a3
=*ptr
++; a4
=*ptr
;
529 alias_port
= GetAliasPort(ftp_link
);
531 switch (ftp_message_type
)
533 case FTP_PORT_COMMAND
:
535 /* Decompose alias port into pair format. */
536 ptr
= (char *) &alias_port
;
537 p1
= *ptr
++; p2
=*ptr
;
539 if (ftp_message_type
== FTP_PORT_COMMAND
) {
540 /* Generate PORT command string. */
541 sprintf(stemp
, "PORT %d,%d,%d,%d,%d,%d\r\n",
544 /* Generate 227 reply string. */
546 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
550 case FTP_EPRT_COMMAND
:
551 /* Generate EPRT command string. */
552 sprintf(stemp
, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
553 a1
,a2
,a3
,a4
,ntohs(alias_port
));
556 /* Generate 229 reply string. */
557 sprintf(stemp
, "229 Entering Extended Passive Mode (|||%d|)\r\n",
562 /* Save string length for IP header modification */
563 slen
= strlen(stemp
);
565 /* Copy modified buffer into IP packet. */
566 sptr
= (char *) pip
; sptr
+= hlen
;
567 strncpy(sptr
, stemp
, maxpacketsize
-hlen
);
570 /* Save information regarding modified seq and ack numbers */
574 SetAckModified(link
);
575 delta
= GetDeltaSeqOut(pip
, link
);
576 AddSeq(pip
, link
, delta
+slen
-dlen
);
579 /* Revise IP header */
583 new_len
= htons(hlen
+ slen
);
584 DifferentialChecksum(&pip
->ip_sum
,
588 pip
->ip_len
= new_len
;
591 /* Compute TCP checksum for revised packet */
593 tc
->th_sum
= TcpChecksum(pip
);
599 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");