]>
git.saurik.com Git - apple/network_cmds.git/blob - alias/alias_ftp.c
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
24 * All rights reserved.
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
35 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * $FreeBSD: src/lib/libalias/alias_ftp.c,v 1.5.2.4 2001/08/21 03:50:25 brian Exp $
52 Alias_ftp.c performs special processing for FTP sessions under
53 TCP. Specifically, when a PORT/EPRT command from the client
54 side or 227/229 reply from the server is sent, it is intercepted
55 and modified. The address is changed to the gateway machine
56 and an aliasing port is used.
58 For this routine to work, the message must fit entirely into a
59 single TCP packet. This is typically the case, but exceptions
60 can easily be envisioned under the actual specifications.
62 Probably the most troubling aspect of the approach taken here is
63 that the new message will typically be a different length, and
64 this causes a certain amount of bookkeeping to keep track of the
65 changes of sequence and acknowledgment numbers, since the client
66 machine is totally unaware of the modification to the TCP stream.
69 References: RFC 959, RFC 2428.
71 Initial version: August, 1996 (cjm)
74 Brian Somers and Martin Renters identified an IP checksum
75 error for modified IP packets.
77 Version 1.7: January 9, 1996 (cjm)
78 Differential checksum computation for change
81 Version 2.1: May, 1997 (cjm)
82 Very minor changes to conform with
83 local/global/function naming conventions
84 within the packet aliasing module.
86 Version 3.1: May, 2000 (eds)
87 Add support for passive mode, alias the 227 replies.
89 See HISTORY file for record of revisions.
96 #include <sys/types.h>
97 #include <netinet/in_systm.h>
98 #include <netinet/in.h>
99 #include <netinet/ip.h>
100 #include <netinet/tcp.h>
102 #include "alias_local.h"
104 #define FTP_CONTROL_PORT_NUMBER 21
105 #define MAX_MESSAGE_SIZE 128
107 enum ftp_message_type
{
115 static int ParseFtpPortCommand(char *, int);
116 static int ParseFtpEprtCommand(char *, int);
117 static int ParseFtp227Reply(char *, int);
118 static int ParseFtp229Reply(char *, int);
119 static void NewFtpMessage(struct ip
*, struct alias_link
*, int, int);
121 static struct in_addr true_addr
; /* in network byte order. */
122 static u_short true_port
; /* in host byte order. */
126 struct ip
*pip
, /* IP packet to examine/patch */
127 struct alias_link
*link
, /* The link to go through (aliased port) */
128 int maxpacketsize
/* The maximum size this packet can grow to (including headers) */)
130 int hlen
, tlen
, dlen
;
133 int ftp_message_type
;
135 /* Calculate data length of TCP packet */
136 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
137 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
138 tlen
= ntohs(pip
->ip_len
);
141 /* Place string pointer and beginning of data */
146 * Check that data length is not too long and previous message was
147 * properly terminated with CRLF.
149 if (dlen
<= MAX_MESSAGE_SIZE
&& GetLastLineCrlfTermed(link
)) {
150 ftp_message_type
= FTP_UNKNOWN_MESSAGE
;
152 if (ntohs(tc
->th_dport
) == FTP_CONTROL_PORT_NUMBER
) {
154 * When aliasing a client, check for the PORT/EPRT command.
156 if (ParseFtpPortCommand(sptr
, dlen
))
157 ftp_message_type
= FTP_PORT_COMMAND
;
158 else if (ParseFtpEprtCommand(sptr
, dlen
))
159 ftp_message_type
= FTP_EPRT_COMMAND
;
162 * When aliasing a server, check for the 227/229 reply.
164 if (ParseFtp227Reply(sptr
, dlen
))
165 ftp_message_type
= FTP_227_REPLY
;
166 else if (ParseFtp229Reply(sptr
, dlen
))
167 ftp_message_type
= FTP_229_REPLY
;
170 if (ftp_message_type
!= FTP_UNKNOWN_MESSAGE
)
171 NewFtpMessage(pip
, link
, maxpacketsize
, ftp_message_type
);
174 /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
176 if (dlen
) { /* only if there's data */
177 sptr
= (char *) pip
; /* start over at beginning */
178 tlen
= ntohs(pip
->ip_len
); /* recalc tlen, pkt may have grown */
179 SetLastLineCrlfTermed(link
,
180 (sptr
[tlen
-2] == '\r') && (sptr
[tlen
-1] == '\n'));
185 ParseFtpPortCommand(char *sptr
, int dlen
)
193 /* Format: "PORT A,D,D,R,PO,RT". */
195 /* Return if data length is too short. */
199 addr
= port
= octet
= 0;
201 for (i
= 0; i
< dlen
; i
++) {
204 case -4: if (ch
== 'P') state
++; else return 0; break;
205 case -3: if (ch
== 'O') state
++; else return 0; break;
206 case -2: if (ch
== 'R') state
++; else return 0; break;
207 case -1: if (ch
== 'T') state
++; else return 0; break;
214 case 1: case 3: case 5: case 7: case 9: case 11:
221 case 2: case 4: case 6: case 8:
223 octet
= 10 * octet
+ ch
- '0';
224 else if (ch
== ',') {
225 addr
= (addr
<< 8) + octet
;
232 octet
= 10 * octet
+ ch
- '0';
233 else if (ch
== ',' || state
== 12) {
234 port
= (port
<< 8) + octet
;
243 true_addr
.s_addr
= htonl(addr
);
251 ParseFtpEprtCommand(char *sptr
, int dlen
)
259 /* Format: "EPRT |1|A.D.D.R|PORT|". */
261 /* Return if data length is too short. */
265 addr
= port
= octet
= 0;
266 delim
= '|'; /* XXX gcc -Wuninitialized */
268 for (i
= 0; i
< dlen
; i
++) {
272 case -4: if (ch
== 'E') state
++; else return 0; break;
273 case -3: if (ch
== 'P') state
++; else return 0; break;
274 case -2: if (ch
== 'R') state
++; else return 0; break;
275 case -1: if (ch
== 'T') state
++; else return 0; break;
284 if (ch
== '1') /* IPv4 address */
295 case 3: case 5: case 7: case 9:
302 case 4: case 6: case 8: case 10:
304 octet
= 10 * octet
+ ch
- '0';
305 else if (ch
== '.' || state
== 10) {
306 addr
= (addr
<< 8) + octet
;
320 port
= 10 * port
+ ch
- '0';
321 else if (ch
== delim
)
330 true_addr
.s_addr
= htonl(addr
);
338 ParseFtp227Reply(char *sptr
, int dlen
)
346 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
348 /* Return if data length is too short. */
352 addr
= port
= octet
= 0;
355 for (i
= 0; i
< dlen
; i
++) {
359 case -3: if (ch
== '2') state
++; else return 0; break;
360 case -2: if (ch
== '2') state
++; else return 0; break;
361 case -1: if (ch
== '7') state
++; else return 0; break;
367 case 1: case 3: case 5: case 7: case 9: case 11:
374 case 2: case 4: case 6: case 8:
376 octet
= 10 * octet
+ ch
- '0';
377 else if (ch
== ',') {
378 addr
= (addr
<< 8) + octet
;
385 octet
= 10 * octet
+ ch
- '0';
386 else if (ch
== ',' || (state
== 12 && ch
== ')')) {
387 port
= (port
<< 8) + octet
;
397 true_addr
.s_addr
= htonl(addr
);
404 ParseFtp229Reply(char *sptr
, int dlen
)
410 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
412 /* Return if data length is too short. */
417 delim
= '|'; /* XXX gcc -Wuninitialized */
420 for (i
= 0; i
< dlen
; i
++) {
424 case -3: if (ch
== '2') state
++; else return 0; break;
425 case -2: if (ch
== '2') state
++; else return 0; break;
426 case -1: if (ch
== '9') state
++; else return 0; break;
451 port
= 10 * port
+ ch
- '0';
452 else if (ch
== delim
)
474 NewFtpMessage(struct ip
*pip
,
475 struct alias_link
*link
,
477 int ftp_message_type
)
479 struct alias_link
*ftp_link
;
481 /* Security checks. */
482 if (ftp_message_type
!= FTP_229_REPLY
&&
483 pip
->ip_src
.s_addr
!= true_addr
.s_addr
)
486 if (true_port
< IPPORT_RESERVED
)
489 /* Establish link to address and port found in FTP control message. */
490 ftp_link
= FindUdpTcpOut(true_addr
, GetDestAddress(link
),
491 htons(true_port
), 0, IPPROTO_TCP
, 1);
493 if (ftp_link
!= NULL
)
495 int slen
, hlen
, tlen
, dlen
;
499 if (ftp_message_type
== FTP_PORT_COMMAND
||
500 ftp_message_type
== FTP_EPRT_COMMAND
) {
501 /* Punch hole in firewall */
502 PunchFWHole(ftp_link
);
506 /* Calculate data length of TCP packet */
507 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
508 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
509 tlen
= ntohs(pip
->ip_len
);
512 /* Create new FTP message. */
514 char stemp
[MAX_MESSAGE_SIZE
+ 1];
518 int a1
, a2
, a3
, a4
, p1
, p2
;
519 struct in_addr alias_address
;
521 /* Decompose alias address into quad format */
522 alias_address
= GetAliasAddress(link
);
523 ptr
= (u_char
*) &alias_address
.s_addr
;
524 a1
= *ptr
++; a2
=*ptr
++; a3
=*ptr
++; a4
=*ptr
;
526 alias_port
= GetAliasPort(ftp_link
);
528 switch (ftp_message_type
)
530 case FTP_PORT_COMMAND
:
532 /* Decompose alias port into pair format. */
533 ptr
= (char *) &alias_port
;
534 p1
= *ptr
++; p2
=*ptr
;
536 if (ftp_message_type
== FTP_PORT_COMMAND
) {
537 /* Generate PORT command string. */
538 sprintf(stemp
, "PORT %d,%d,%d,%d,%d,%d\r\n",
541 /* Generate 227 reply string. */
543 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
547 case FTP_EPRT_COMMAND
:
548 /* Generate EPRT command string. */
549 sprintf(stemp
, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
550 a1
,a2
,a3
,a4
,ntohs(alias_port
));
553 /* Generate 229 reply string. */
554 sprintf(stemp
, "229 Entering Extended Passive Mode (|||%d|)\r\n",
559 /* Save string length for IP header modification */
560 slen
= strlen(stemp
);
562 /* Copy modified buffer into IP packet. */
563 sptr
= (char *) pip
; sptr
+= hlen
;
564 strncpy(sptr
, stemp
, maxpacketsize
-hlen
);
567 /* Save information regarding modified seq and ack numbers */
571 SetAckModified(link
);
572 delta
= GetDeltaSeqOut(pip
, link
);
573 AddSeq(pip
, link
, delta
+slen
-dlen
);
576 /* Revise IP header */
580 new_len
= htons(hlen
+ slen
);
581 DifferentialChecksum(&pip
->ip_sum
,
585 pip
->ip_len
= new_len
;
588 /* Compute TCP checksum for revised packet */
590 tc
->th_sum
= TcpChecksum(pip
);
596 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");