]>
git.saurik.com Git - apple/network_cmds.git/blob - alias/alias_ftp.c
77c4d8cb4d840f2d4354260beaf706110ee1748d
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
25 * All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * $FreeBSD: src/lib/libalias/alias_ftp.c,v 1.5.2.4 2001/08/21 03:50:25 brian Exp $
53 Alias_ftp.c performs special processing for FTP sessions under
54 TCP. Specifically, when a PORT/EPRT command from the client
55 side or 227/229 reply from the server is sent, it is intercepted
56 and modified. The address is changed to the gateway machine
57 and an aliasing port is used.
59 For this routine to work, the message must fit entirely into a
60 single TCP packet. This is typically the case, but exceptions
61 can easily be envisioned under the actual specifications.
63 Probably the most troubling aspect of the approach taken here is
64 that the new message will typically be a different length, and
65 this causes a certain amount of bookkeeping to keep track of the
66 changes of sequence and acknowledgment numbers, since the client
67 machine is totally unaware of the modification to the TCP stream.
70 References: RFC 959, RFC 2428.
72 Initial version: August, 1996 (cjm)
75 Brian Somers and Martin Renters identified an IP checksum
76 error for modified IP packets.
78 Version 1.7: January 9, 1996 (cjm)
79 Differential checksum computation for change
82 Version 2.1: May, 1997 (cjm)
83 Very minor changes to conform with
84 local/global/function naming conventions
85 within the packet aliasing module.
87 Version 3.1: May, 2000 (eds)
88 Add support for passive mode, alias the 227 replies.
90 See HISTORY file for record of revisions.
97 #include <sys/types.h>
98 #include <netinet/in_systm.h>
99 #include <netinet/in.h>
100 #include <netinet/ip.h>
101 #include <netinet/tcp.h>
103 #include "alias_local.h"
105 #define FTP_CONTROL_PORT_NUMBER 21
106 #define MAX_MESSAGE_SIZE 128
108 enum ftp_message_type
{
116 static int ParseFtpPortCommand(char *, int);
117 static int ParseFtpEprtCommand(char *, int);
118 static int ParseFtp227Reply(char *, int);
119 static int ParseFtp229Reply(char *, int);
120 static void NewFtpMessage(struct ip
*, struct alias_link
*, int, int);
122 static struct in_addr true_addr
; /* in network byte order. */
123 static u_short true_port
; /* in host byte order. */
127 struct ip
*pip
, /* IP packet to examine/patch */
128 struct alias_link
*link
, /* The link to go through (aliased port) */
129 int maxpacketsize
/* The maximum size this packet can grow to (including headers) */)
131 int hlen
, tlen
, dlen
;
134 int ftp_message_type
;
136 /* Calculate data length of TCP packet */
137 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
138 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
139 tlen
= ntohs(pip
->ip_len
);
142 /* Place string pointer and beginning of data */
147 * Check that data length is not too long and previous message was
148 * properly terminated with CRLF.
150 if (dlen
<= MAX_MESSAGE_SIZE
&& GetLastLineCrlfTermed(link
)) {
151 ftp_message_type
= FTP_UNKNOWN_MESSAGE
;
153 if (ntohs(tc
->th_dport
) == FTP_CONTROL_PORT_NUMBER
) {
155 * When aliasing a client, check for the PORT/EPRT command.
157 if (ParseFtpPortCommand(sptr
, dlen
))
158 ftp_message_type
= FTP_PORT_COMMAND
;
159 else if (ParseFtpEprtCommand(sptr
, dlen
))
160 ftp_message_type
= FTP_EPRT_COMMAND
;
163 * When aliasing a server, check for the 227/229 reply.
165 if (ParseFtp227Reply(sptr
, dlen
))
166 ftp_message_type
= FTP_227_REPLY
;
167 else if (ParseFtp229Reply(sptr
, dlen
))
168 ftp_message_type
= FTP_229_REPLY
;
171 if (ftp_message_type
!= FTP_UNKNOWN_MESSAGE
)
172 NewFtpMessage(pip
, link
, maxpacketsize
, ftp_message_type
);
175 /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
177 if (dlen
) { /* only if there's data */
178 sptr
= (char *) pip
; /* start over at beginning */
179 tlen
= ntohs(pip
->ip_len
); /* recalc tlen, pkt may have grown */
180 SetLastLineCrlfTermed(link
,
181 (sptr
[tlen
-2] == '\r') && (sptr
[tlen
-1] == '\n'));
186 ParseFtpPortCommand(char *sptr
, int dlen
)
194 /* Format: "PORT A,D,D,R,PO,RT". */
196 /* Return if data length is too short. */
200 addr
= port
= octet
= 0;
202 for (i
= 0; i
< dlen
; i
++) {
205 case -4: if (ch
== 'P') state
++; else return 0; break;
206 case -3: if (ch
== 'O') state
++; else return 0; break;
207 case -2: if (ch
== 'R') state
++; else return 0; break;
208 case -1: if (ch
== 'T') state
++; else return 0; break;
215 case 1: case 3: case 5: case 7: case 9: case 11:
222 case 2: case 4: case 6: case 8:
224 octet
= 10 * octet
+ ch
- '0';
225 else if (ch
== ',') {
226 addr
= (addr
<< 8) + octet
;
233 octet
= 10 * octet
+ ch
- '0';
234 else if (ch
== ',' || state
== 12) {
235 port
= (port
<< 8) + octet
;
244 true_addr
.s_addr
= htonl(addr
);
252 ParseFtpEprtCommand(char *sptr
, int dlen
)
260 /* Format: "EPRT |1|A.D.D.R|PORT|". */
262 /* Return if data length is too short. */
266 addr
= port
= octet
= 0;
267 delim
= '|'; /* XXX gcc -Wuninitialized */
269 for (i
= 0; i
< dlen
; i
++) {
273 case -4: if (ch
== 'E') state
++; else return 0; break;
274 case -3: if (ch
== 'P') state
++; else return 0; break;
275 case -2: if (ch
== 'R') state
++; else return 0; break;
276 case -1: if (ch
== 'T') state
++; else return 0; break;
285 if (ch
== '1') /* IPv4 address */
296 case 3: case 5: case 7: case 9:
303 case 4: case 6: case 8: case 10:
305 octet
= 10 * octet
+ ch
- '0';
306 else if (ch
== '.' || state
== 10) {
307 addr
= (addr
<< 8) + octet
;
321 port
= 10 * port
+ ch
- '0';
322 else if (ch
== delim
)
331 true_addr
.s_addr
= htonl(addr
);
339 ParseFtp227Reply(char *sptr
, int dlen
)
347 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
349 /* Return if data length is too short. */
353 addr
= port
= octet
= 0;
356 for (i
= 0; i
< dlen
; i
++) {
360 case -3: if (ch
== '2') state
++; else return 0; break;
361 case -2: if (ch
== '2') state
++; else return 0; break;
362 case -1: if (ch
== '7') state
++; else return 0; break;
368 case 1: case 3: case 5: case 7: case 9: case 11:
375 case 2: case 4: case 6: case 8:
377 octet
= 10 * octet
+ ch
- '0';
378 else if (ch
== ',') {
379 addr
= (addr
<< 8) + octet
;
386 octet
= 10 * octet
+ ch
- '0';
387 else if (ch
== ',' || (state
== 12 && ch
== ')')) {
388 port
= (port
<< 8) + octet
;
398 true_addr
.s_addr
= htonl(addr
);
405 ParseFtp229Reply(char *sptr
, int dlen
)
411 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
413 /* Return if data length is too short. */
418 delim
= '|'; /* XXX gcc -Wuninitialized */
421 for (i
= 0; i
< dlen
; i
++) {
425 case -3: if (ch
== '2') state
++; else return 0; break;
426 case -2: if (ch
== '2') state
++; else return 0; break;
427 case -1: if (ch
== '9') state
++; else return 0; break;
452 port
= 10 * port
+ ch
- '0';
453 else if (ch
== delim
)
475 NewFtpMessage(struct ip
*pip
,
476 struct alias_link
*link
,
478 int ftp_message_type
)
480 struct alias_link
*ftp_link
;
482 /* Security checks. */
483 if (ftp_message_type
!= FTP_229_REPLY
&&
484 pip
->ip_src
.s_addr
!= true_addr
.s_addr
)
487 if (true_port
< IPPORT_RESERVED
)
490 /* Establish link to address and port found in FTP control message. */
491 ftp_link
= FindUdpTcpOut(true_addr
, GetDestAddress(link
),
492 htons(true_port
), 0, IPPROTO_TCP
, 1);
494 if (ftp_link
!= NULL
)
496 int slen
, hlen
, tlen
, dlen
;
500 if (ftp_message_type
== FTP_PORT_COMMAND
||
501 ftp_message_type
== FTP_EPRT_COMMAND
) {
502 /* Punch hole in firewall */
503 PunchFWHole(ftp_link
);
507 /* Calculate data length of TCP packet */
508 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
509 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
510 tlen
= ntohs(pip
->ip_len
);
513 /* Create new FTP message. */
515 char stemp
[MAX_MESSAGE_SIZE
+ 1];
519 int a1
, a2
, a3
, a4
, p1
, p2
;
520 struct in_addr alias_address
;
522 /* Decompose alias address into quad format */
523 alias_address
= GetAliasAddress(link
);
524 ptr
= (u_char
*) &alias_address
.s_addr
;
525 a1
= *ptr
++; a2
=*ptr
++; a3
=*ptr
++; a4
=*ptr
;
527 alias_port
= GetAliasPort(ftp_link
);
529 switch (ftp_message_type
)
531 case FTP_PORT_COMMAND
:
533 /* Decompose alias port into pair format. */
534 ptr
= (char *) &alias_port
;
535 p1
= *ptr
++; p2
=*ptr
;
537 if (ftp_message_type
== FTP_PORT_COMMAND
) {
538 /* Generate PORT command string. */
539 sprintf(stemp
, "PORT %d,%d,%d,%d,%d,%d\r\n",
542 /* Generate 227 reply string. */
544 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
548 case FTP_EPRT_COMMAND
:
549 /* Generate EPRT command string. */
550 sprintf(stemp
, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
551 a1
,a2
,a3
,a4
,ntohs(alias_port
));
554 /* Generate 229 reply string. */
555 sprintf(stemp
, "229 Entering Extended Passive Mode (|||%d|)\r\n",
560 /* Save string length for IP header modification */
561 slen
= strlen(stemp
);
563 /* Copy modified buffer into IP packet. */
564 sptr
= (char *) pip
; sptr
+= hlen
;
565 strncpy(sptr
, stemp
, maxpacketsize
-hlen
);
568 /* Save information regarding modified seq and ack numbers */
572 SetAckModified(link
);
573 delta
= GetDeltaSeqOut(pip
, link
);
574 AddSeq(pip
, link
, delta
+slen
-dlen
);
577 /* Revise IP header */
581 new_len
= htons(hlen
+ slen
);
582 DifferentialChecksum(&pip
->ip_sum
,
586 pip
->ip_len
= new_len
;
589 /* Compute TCP checksum for revised packet */
591 tc
->th_sum
= TcpChecksum(pip
);
597 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");