xref: /freebsd/sys/netinet/libalias/alias_ftp.c (revision 33b77e2decd50e53798014b70bf7ca3bdc4c0c7e)
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 /* Punch hole in firewall */
156         PunchFWHole(ftp_link);
157 
158 /* Calculate data length of TCP packet */
159         tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
160         hlen = (pip->ip_hl + tc->th_off) << 2;
161         tlen = ntohs(pip->ip_len);
162         dlen = tlen - hlen;
163 
164 /* Create new PORT command */
165         {
166             char stemp[80];
167             char *sptr;
168             u_short alias_port;
169             u_char *ptr;
170             int a1, a2, a3, a4, p1, p2;
171             struct in_addr alias_address;
172 
173 /* Decompose alias address into quad format */
174             alias_address = GetAliasAddress(link);
175             ptr = (u_char *) &alias_address.s_addr;
176             a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr;
177 
178 /* Decompose alias port into pair format */
179             alias_port = GetAliasPort(ftp_link);
180             ptr = (char *) &alias_port;
181             p1 = *ptr++; p2=*ptr;
182 
183 /* Generate command string */
184             sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
185                      a1,a2,a3,a4,p1,p2);
186 
187 /* Save string length for IP header modification */
188             slen = strlen(stemp);
189 
190 /* Copy into IP packet */
191             sptr = (char *) pip; sptr += hlen;
192             strncpy(sptr, stemp, maxpacketsize-hlen);
193         }
194 
195 /* Save information regarding modified seq and ack numbers */
196         {
197             int delta;
198 
199             SetAckModified(link);
200             delta = GetDeltaSeqOut(pip, link);
201             AddSeq(pip, link, delta+slen-dlen);
202         }
203 
204 /* Revise IP header */
205         {
206             u_short new_len;
207 
208             new_len = htons(hlen + slen);
209             DifferentialChecksum(&pip->ip_sum,
210                                  &new_len,
211                                  &pip->ip_len,
212                                  1);
213             pip->ip_len = new_len;
214         }
215 
216 /* Compute TCP checksum for revised packet */
217         tc->th_sum = 0;
218         tc->th_sum = TcpChecksum(pip);
219     }
220     else
221     {
222         fprintf(stderr,
223         "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
224     }
225 }
226