]>
git.saurik.com Git - apple/network_cmds.git/blob - alias/alias_ftp.c
2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * $FreeBSD: src/lib/libalias/alias_ftp.c,v 1.5.2.4 2001/08/21 03:50:25 brian Exp $
59 Alias_ftp.c performs special processing for FTP sessions under
60 TCP. Specifically, when a PORT/EPRT command from the client
61 side or 227/229 reply from the server is sent, it is intercepted
62 and modified. The address is changed to the gateway machine
63 and an aliasing port is used.
65 For this routine to work, the message must fit entirely into a
66 single TCP packet. This is typically the case, but exceptions
67 can easily be envisioned under the actual specifications.
69 Probably the most troubling aspect of the approach taken here is
70 that the new message will typically be a different length, and
71 this causes a certain amount of bookkeeping to keep track of the
72 changes of sequence and acknowledgment numbers, since the client
73 machine is totally unaware of the modification to the TCP stream.
76 References: RFC 959, RFC 2428.
78 Initial version: August, 1996 (cjm)
81 Brian Somers and Martin Renters identified an IP checksum
82 error for modified IP packets.
84 Version 1.7: January 9, 1996 (cjm)
85 Differential checksum computation for change
88 Version 2.1: May, 1997 (cjm)
89 Very minor changes to conform with
90 local/global/function naming conventions
91 within the packet aliasing module.
93 Version 3.1: May, 2000 (eds)
94 Add support for passive mode, alias the 227 replies.
96 See HISTORY file for record of revisions.
103 #include <sys/types.h>
104 #include <netinet/in_systm.h>
105 #include <netinet/in.h>
106 #include <netinet/ip.h>
107 #include <netinet/tcp.h>
109 #include "alias_local.h"
111 #define FTP_CONTROL_PORT_NUMBER 21
112 #define MAX_MESSAGE_SIZE 128
114 enum ftp_message_type
{
122 static int ParseFtpPortCommand(char *, int);
123 static int ParseFtpEprtCommand(char *, int);
124 static int ParseFtp227Reply(char *, int);
125 static int ParseFtp229Reply(char *, int);
126 static void NewFtpMessage(struct ip
*, struct alias_link
*, int, int);
128 static struct in_addr true_addr
; /* in network byte order. */
129 static u_short true_port
; /* in host byte order. */
133 struct ip
*pip
, /* IP packet to examine/patch */
134 struct alias_link
*link
, /* The link to go through (aliased port) */
135 int maxpacketsize
/* The maximum size this packet can grow to (including headers) */)
137 int hlen
, tlen
, dlen
;
140 int ftp_message_type
;
142 /* Calculate data length of TCP packet */
143 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
144 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
145 tlen
= ntohs(pip
->ip_len
);
148 /* Place string pointer and beginning of data */
153 * Check that data length is not too long and previous message was
154 * properly terminated with CRLF.
156 if (dlen
<= MAX_MESSAGE_SIZE
&& GetLastLineCrlfTermed(link
)) {
157 ftp_message_type
= FTP_UNKNOWN_MESSAGE
;
159 if (ntohs(tc
->th_dport
) == FTP_CONTROL_PORT_NUMBER
) {
161 * When aliasing a client, check for the PORT/EPRT command.
163 if (ParseFtpPortCommand(sptr
, dlen
))
164 ftp_message_type
= FTP_PORT_COMMAND
;
165 else if (ParseFtpEprtCommand(sptr
, dlen
))
166 ftp_message_type
= FTP_EPRT_COMMAND
;
169 * When aliasing a server, check for the 227/229 reply.
171 if (ParseFtp227Reply(sptr
, dlen
))
172 ftp_message_type
= FTP_227_REPLY
;
173 else if (ParseFtp229Reply(sptr
, dlen
))
174 ftp_message_type
= FTP_229_REPLY
;
177 if (ftp_message_type
!= FTP_UNKNOWN_MESSAGE
)
178 NewFtpMessage(pip
, link
, maxpacketsize
, ftp_message_type
);
181 /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
183 if (dlen
) { /* only if there's data */
184 sptr
= (char *) pip
; /* start over at beginning */
185 tlen
= ntohs(pip
->ip_len
); /* recalc tlen, pkt may have grown */
186 SetLastLineCrlfTermed(link
,
187 (sptr
[tlen
-2] == '\r') && (sptr
[tlen
-1] == '\n'));
192 ParseFtpPortCommand(char *sptr
, int dlen
)
200 /* Format: "PORT A,D,D,R,PO,RT". */
202 /* Return if data length is too short. */
206 addr
= port
= octet
= 0;
208 for (i
= 0; i
< dlen
; i
++) {
211 case -4: if (ch
== 'P') state
++; else return 0; break;
212 case -3: if (ch
== 'O') state
++; else return 0; break;
213 case -2: if (ch
== 'R') state
++; else return 0; break;
214 case -1: if (ch
== 'T') state
++; else return 0; break;
221 case 1: case 3: case 5: case 7: case 9: case 11:
228 case 2: case 4: case 6: case 8:
230 octet
= 10 * octet
+ ch
- '0';
231 else if (ch
== ',') {
232 addr
= (addr
<< 8) + octet
;
239 octet
= 10 * octet
+ ch
- '0';
240 else if (ch
== ',' || state
== 12) {
241 port
= (port
<< 8) + octet
;
250 true_addr
.s_addr
= htonl(addr
);
258 ParseFtpEprtCommand(char *sptr
, int dlen
)
266 /* Format: "EPRT |1|A.D.D.R|PORT|". */
268 /* Return if data length is too short. */
272 addr
= port
= octet
= 0;
273 delim
= '|'; /* XXX gcc -Wuninitialized */
275 for (i
= 0; i
< dlen
; i
++) {
279 case -4: if (ch
== 'E') state
++; else return 0; break;
280 case -3: if (ch
== 'P') state
++; else return 0; break;
281 case -2: if (ch
== 'R') state
++; else return 0; break;
282 case -1: if (ch
== 'T') state
++; else return 0; break;
291 if (ch
== '1') /* IPv4 address */
302 case 3: case 5: case 7: case 9:
309 case 4: case 6: case 8: case 10:
311 octet
= 10 * octet
+ ch
- '0';
312 else if (ch
== '.' || state
== 10) {
313 addr
= (addr
<< 8) + octet
;
327 port
= 10 * port
+ ch
- '0';
328 else if (ch
== delim
)
337 true_addr
.s_addr
= htonl(addr
);
345 ParseFtp227Reply(char *sptr
, int dlen
)
353 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
355 /* Return if data length is too short. */
359 addr
= port
= octet
= 0;
362 for (i
= 0; i
< dlen
; i
++) {
366 case -3: if (ch
== '2') state
++; else return 0; break;
367 case -2: if (ch
== '2') state
++; else return 0; break;
368 case -1: if (ch
== '7') state
++; else return 0; break;
374 case 1: case 3: case 5: case 7: case 9: case 11:
381 case 2: case 4: case 6: case 8:
383 octet
= 10 * octet
+ ch
- '0';
384 else if (ch
== ',') {
385 addr
= (addr
<< 8) + octet
;
392 octet
= 10 * octet
+ ch
- '0';
393 else if (ch
== ',' || (state
== 12 && ch
== ')')) {
394 port
= (port
<< 8) + octet
;
404 true_addr
.s_addr
= htonl(addr
);
411 ParseFtp229Reply(char *sptr
, int dlen
)
417 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
419 /* Return if data length is too short. */
424 delim
= '|'; /* XXX gcc -Wuninitialized */
427 for (i
= 0; i
< dlen
; i
++) {
431 case -3: if (ch
== '2') state
++; else return 0; break;
432 case -2: if (ch
== '2') state
++; else return 0; break;
433 case -1: if (ch
== '9') state
++; else return 0; break;
458 port
= 10 * port
+ ch
- '0';
459 else if (ch
== delim
)
481 NewFtpMessage(struct ip
*pip
,
482 struct alias_link
*link
,
484 int ftp_message_type
)
486 struct alias_link
*ftp_link
;
488 /* Security checks. */
489 if (ftp_message_type
!= FTP_229_REPLY
&&
490 pip
->ip_src
.s_addr
!= true_addr
.s_addr
)
493 if (true_port
< IPPORT_RESERVED
)
496 /* Establish link to address and port found in FTP control message. */
497 ftp_link
= FindUdpTcpOut(true_addr
, GetDestAddress(link
),
498 htons(true_port
), 0, IPPROTO_TCP
, 1);
500 if (ftp_link
!= NULL
)
502 int slen
, hlen
, tlen
, dlen
;
506 if (ftp_message_type
== FTP_PORT_COMMAND
||
507 ftp_message_type
== FTP_EPRT_COMMAND
) {
508 /* Punch hole in firewall */
509 PunchFWHole(ftp_link
);
513 /* Calculate data length of TCP packet */
514 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
515 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
516 tlen
= ntohs(pip
->ip_len
);
519 /* Create new FTP message. */
521 char stemp
[MAX_MESSAGE_SIZE
+ 1];
525 int a1
, a2
, a3
, a4
, p1
, p2
;
526 struct in_addr alias_address
;
528 /* Decompose alias address into quad format */
529 alias_address
= GetAliasAddress(link
);
530 ptr
= (u_char
*) &alias_address
.s_addr
;
531 a1
= *ptr
++; a2
=*ptr
++; a3
=*ptr
++; a4
=*ptr
;
533 alias_port
= GetAliasPort(ftp_link
);
535 switch (ftp_message_type
)
537 case FTP_PORT_COMMAND
:
539 /* Decompose alias port into pair format. */
540 ptr
= (u_char
*) &alias_port
;
541 p1
= *ptr
++; p2
=*ptr
;
543 if (ftp_message_type
== FTP_PORT_COMMAND
) {
544 /* Generate PORT command string. */
545 snprintf(stemp
, sizeof(stemp
), "PORT %d,%d,%d,%d,%d,%d\r\n",
548 /* Generate 227 reply string. */
549 snprintf(stemp
, sizeof(stemp
),
550 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
554 case FTP_EPRT_COMMAND
:
555 /* Generate EPRT command string. */
556 snprintf(stemp
, sizeof(stemp
), "EPRT |1|%d.%d.%d.%d|%d|\r\n",
557 a1
,a2
,a3
,a4
,ntohs(alias_port
));
560 /* Generate 229 reply string. */
561 snprintf(stemp
, sizeof(stemp
), "229 Entering Extended Passive Mode (|||%d|)\r\n",
566 /* Save string length for IP header modification */
567 slen
= strlen(stemp
);
569 /* Copy modified buffer into IP packet. */
570 sptr
= (char *) pip
; sptr
+= hlen
;
571 strncpy(sptr
, stemp
, maxpacketsize
-hlen
);
574 /* Save information regarding modified seq and ack numbers */
578 SetAckModified(link
);
579 delta
= GetDeltaSeqOut(pip
, link
);
580 AddSeq(pip
, link
, delta
+slen
-dlen
);
583 /* Revise IP header */
587 new_len
= htons(hlen
+ slen
);
588 DifferentialChecksum(&pip
->ip_sum
,
592 pip
->ip_len
= new_len
;
595 /* Compute TCP checksum for revised packet */
597 tc
->th_sum
= TcpChecksum(pip
);
603 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");