xref: /freebsd/sys/netinet/libalias/alias_ftp.c (revision 11afcc8f9f96d657b8e6f7547c02c1957331fc96)
1 /*
2     Alias_ftp.c performs special processing for FTP sessions under
3     TCP.  Specifically, when a PORT command from the client side
4     is sent, it is intercepted and modified.  The address is changed
5     to the gateway machine and an aliasing port is used.
6 
7     For this routine to work, the PORT command must fit entirely
8     into a single TCP packet.  This is typically the case, but exceptions
9     can easily be envisioned under the actual specifications.
10 
11     Probably the most troubling aspect of the approach taken here is
12     that the new PORT command will typically be a different length, and
13     this causes a certain amount of bookkeeping to keep track of the
14     changes of sequence and acknowledgment numbers, since the client
15     machine is totally unaware of the modification to the TCP stream.
16 
17 
18     This software is placed into the public domain with no restrictions
19     on its distribution.
20 
21     Initial version:  August, 1996  (cjm)
22 
23     Version 1.6
24          Brian Somers and Martin Renters identified an IP checksum
25          error for modified IP packets.
26 
27     Version 1.7:  January 9, 1996 (cjm)
28          Differental checksum computation for change
29          in IP packet length.
30 
31     Version 2.1:  May, 1997 (cjm)
32          Very minor changes to conform with
33          local/global/function naming conventions
34          withing the packet alising module.
35 
36     See HISTORY file for record of revisions.
37 */
38 
39 /* Includes */
40 #include <ctype.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <netinet/in_systm.h>
45 #include <netinet/in.h>
46 #include <netinet/ip.h>
47 #include <netinet/tcp.h>
48 
49 #include "alias_local.h"
50 
51 static void NewFtpPortCommand(struct ip *, struct alias_link *, struct in_addr, u_short, int);
52 
53 
54 
55 void
56 AliasHandleFtpOut(
57 struct ip *pip,	  /* IP packet to examine/patch */
58 struct alias_link *link, /* The link to go through (aliased port) */
59 int maxpacketsize  /* The maximum size this packet can grow to (including headers) */)
60 {
61     int hlen, tlen, dlen;
62     struct in_addr true_addr;
63     u_short true_port;
64     char *sptr;
65     struct tcphdr *tc;
66 
67 /* Calculate data length of TCP packet */
68     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
69     hlen = (pip->ip_hl + tc->th_off) << 2;
70     tlen = ntohs(pip->ip_len);
71     dlen = tlen - hlen;
72 
73 /* Return is data length is too long or too short */
74     if (dlen<10 || dlen>80)
75         return;
76 
77 /* Place string pointer and beginning of data */
78     sptr = (char *) pip;
79     sptr += hlen;
80 
81 /* Parse through string using state diagram method */
82     {
83         char ch, zero;
84         int i, state;
85         u_long a1, a2, a3, a4;
86         u_short p1, p2;
87 
88         a1=0; a2=0; a3=0; a4=0; p1=0; p2=0;
89         zero = '0';
90         state=-4;
91         for (i=0; i<dlen; i++)
92         {
93             ch = sptr[i];
94             switch (state)
95             {
96                 case -4: if (ch == 'P') state=-3; else return; break;
97                 case -3: if (ch == 'O') state=-2; else return; break;
98                 case -2: if (ch == 'R') state=-1; else return; break;
99                 case -1: if (ch == 'T') state= 0; else return; break;
100 
101                 case 0 :
102                     if (isdigit(ch)) {a1=ch-zero; state=1 ;} break;
103                 case 1 :
104                     if (isdigit(ch)) a1=10*a1+ch-zero; else state=2 ; break;
105                 case 2 :
106                     if (isdigit(ch)) {a2=ch-zero; state=3 ;} break;
107                 case 3 :
108                     if (isdigit(ch)) a2=10*a2+ch-zero; else state=4 ; break;
109                 case 4 :
110                     if (isdigit(ch)) {a3=ch-zero; state=5 ;} break;
111                 case 5 :
112                     if (isdigit(ch)) a3=10*a3+ch-zero; else state=6 ; break;
113                 case 6 :
114                     if (isdigit(ch)) {a4=ch-zero; state=7 ;} break;
115                 case 7 :
116                     if (isdigit(ch)) a4=10*a4+ch-zero; else state=8 ; break;
117                 case 8 :
118                     if (isdigit(ch)) {p1=ch-zero; state=9 ;} break;
119                 case 9 :
120                     if (isdigit(ch)) p1=10*p1+ch-zero; else state=10; break;
121                 case 10:
122                     if (isdigit(ch)) {p2=ch-zero; state=11;} break;
123                 case 11:
124                     if (isdigit(ch)) p2=10*p2+ch-zero; break;
125             }
126         }
127 
128         if (state == 11)
129         {
130             true_port = htons((p1<<8) + p2);
131             true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4);
132             NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize);
133         }
134     }
135 }
136 
137 static void
138 NewFtpPortCommand(struct ip *pip,
139                   struct alias_link *link,
140                   struct in_addr true_addr,
141                   u_short true_port,
142                   int maxpacketsize)
143 {
144     struct alias_link *ftp_link;
145 
146 /* Establish link to address and port found in PORT command */
147     ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link),
148                              true_port, 0, IPPROTO_TCP);
149 
150     if (ftp_link != NULL)
151     {
152         int slen, hlen, tlen, dlen;
153         struct tcphdr *tc;
154 
155 #ifndef NO_FW_PUNCH
156 /* Punch hole in firewall */
157         PunchFWHole(ftp_link);
158 #endif
159 
160 /* Calculate data length of TCP packet */
161         tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
162         hlen = (pip->ip_hl + tc->th_off) << 2;
163         tlen = ntohs(pip->ip_len);
164         dlen = tlen - hlen;
165 
166 /* Create new PORT command */
167         {
168             char stemp[80];
169             char *sptr;
170             u_short alias_port;
171             u_char *ptr;
172             int a1, a2, a3, a4, p1, p2;
173             struct in_addr alias_address;
174 
175 /* Decompose alias address into quad format */
176             alias_address = GetAliasAddress(link);
177             ptr = (u_char *) &alias_address.s_addr;
178             a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr;
179 
180 /* Decompose alias port into pair format */
181             alias_port = GetAliasPort(ftp_link);
182             ptr = (char *) &alias_port;
183             p1 = *ptr++; p2=*ptr;
184 
185 /* Generate command string */
186             sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
187                      a1,a2,a3,a4,p1,p2);
188 
189 /* Save string length for IP header modification */
190             slen = strlen(stemp);
191 
192 /* Copy into IP packet */
193             sptr = (char *) pip; sptr += hlen;
194             strncpy(sptr, stemp, maxpacketsize-hlen);
195         }
196 
197 /* Save information regarding modified seq and ack numbers */
198         {
199             int delta;
200 
201             SetAckModified(link);
202             delta = GetDeltaSeqOut(pip, link);
203             AddSeq(pip, link, delta+slen-dlen);
204         }
205 
206 /* Revise IP header */
207         {
208             u_short new_len;
209 
210             new_len = htons(hlen + slen);
211             DifferentialChecksum(&pip->ip_sum,
212                                  &new_len,
213                                  &pip->ip_len,
214                                  1);
215             pip->ip_len = new_len;
216         }
217 
218 /* Compute TCP checksum for revised packet */
219         tc->th_sum = 0;
220         tc->th_sum = TcpChecksum(pip);
221     }
222     else
223     {
224         fprintf(stderr,
225         "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
226     }
227 }
228