xref: /freebsd/sys/netinet/libalias/alias_ftp.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
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     This version also supports the EPRT command, which is functionally
18     equivalent to the PORT command, but was designed to support both
19     IPv4 and IPv6 addresses.  See RFC 2428 for specifications.
20 
21 
22     This software is placed into the public domain with no restrictions
23     on its distribution.
24 
25     Initial version:  August, 1996  (cjm)
26 
27     Version 1.6
28          Brian Somers and Martin Renters identified an IP checksum
29          error for modified IP packets.
30 
31     Version 1.7:  January 9, 1996 (cjm)
32          Differential checksum computation for change
33          in IP packet length.
34 
35     Version 2.1:  May, 1997 (cjm)
36          Very minor changes to conform with
37          local/global/function naming conventions
38          within the packet aliasing module.
39 
40     See HISTORY file for record of revisions.
41 
42     $FreeBSD$
43 */
44 
45 /* Includes */
46 #include <ctype.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <sys/types.h>
50 #include <netinet/in_systm.h>
51 #include <netinet/in.h>
52 #include <netinet/ip.h>
53 #include <netinet/tcp.h>
54 
55 #include "alias_local.h"
56 
57 static int  ParseFtpPortCommand(char *, int, struct ip *, struct alias_link *, int);
58 static void ParseFtpEprtCommand(char *, int, struct ip *, struct alias_link *, int);
59 static void NewFtpPortCommand(struct ip *, struct alias_link *, struct in_addr, u_short, int, int);
60 
61 
62 
63 void
64 AliasHandleFtpOut(
65 struct ip *pip,	  /* IP packet to examine/patch */
66 struct alias_link *link, /* The link to go through (aliased port) */
67 int maxpacketsize  /* The maximum size this packet can grow to (including headers) */)
68 {
69     int hlen, tlen, dlen;
70     char *sptr;
71     struct tcphdr *tc;
72 
73 /* Calculate data length of TCP packet */
74     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
75     hlen = (pip->ip_hl + tc->th_off) << 2;
76     tlen = ntohs(pip->ip_len);
77     dlen = tlen - hlen;
78 
79 /* Return if data length is too long or too short */
80     if (dlen<10 || dlen>80)
81         return;
82 
83 /* Place string pointer and beginning of data */
84     sptr = (char *) pip;
85     sptr += hlen;
86 
87 /* Parse through string using state diagram method */
88     if (!ParseFtpPortCommand(sptr, dlen, pip, link, maxpacketsize))
89 	ParseFtpEprtCommand(sptr, dlen, pip, link, maxpacketsize);
90 }
91 
92 static int
93 ParseFtpPortCommand(
94 char *sptr,
95 int dlen,
96 struct ip *pip,	  /* IP packet to examine/patch */
97 struct alias_link *link, /* The link to go through (aliased port) */
98 int maxpacketsize  /* The maximum size this packet can grow to (including headers) */)
99 {
100     struct in_addr true_addr;
101     u_short true_port;
102     char ch;
103     int i, state;
104     u_long a1, a2, a3, a4;
105     u_short p1, p2;
106 
107     a1=0; a2=0; a3=0; a4=0; p1=0; p2=0;
108     state=-4;
109     for (i=0; i<dlen; i++)
110     {
111 	ch = sptr[i];
112 	switch (state)
113 	{
114 	case -4: if (ch == 'P') state++; else return 0; break;
115 	case -3: if (ch == 'O') state++; else return 0; break;
116 	case -2: if (ch == 'R') state++; else return 0; break;
117 	case -1: if (ch == 'T') state++; else return 0; break;
118 
119 	case 0 :
120 	    if (isdigit(ch)) {a1=ch-'0'; state++;} break;
121 	case 1 :
122 	    if (isdigit(ch)) a1=10*a1+ch-'0'; else state++; break;
123 	case 2 :
124 	    if (isdigit(ch)) {a2=ch-'0'; state++;} break;
125 	case 3 :
126 	    if (isdigit(ch)) a2=10*a2+ch-'0'; else state++; break;
127 	case 4 :
128 	    if (isdigit(ch)) {a3=ch-'0'; state++;} break;
129 	case 5 :
130 	    if (isdigit(ch)) a3=10*a3+ch-'0'; else state++; break;
131 	case 6 :
132 	    if (isdigit(ch)) {a4=ch-'0'; state++;} break;
133 	case 7 :
134 	    if (isdigit(ch)) a4=10*a4+ch-'0'; else state++; break;
135 	case 8 :
136 	    if (isdigit(ch)) {p1=ch-'0'; state++;} break;
137 	case 9 :
138 	    if (isdigit(ch)) p1=10*p1+ch-'0'; else state++; break;
139 	case 10:
140 	    if (isdigit(ch)) {p2=ch-'0'; state++;} break;
141 	case 11:
142 	    if (isdigit(ch)) p2=10*p2+ch-'0'; break;
143 	}
144     }
145 
146     if (state == 11)
147     {
148 	true_port = htons((p1<<8) + p2);
149 	true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4);
150 	NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize, 0);
151 	return 1;
152     }
153     else
154 	return 0;
155 }
156 
157 static void
158 ParseFtpEprtCommand(
159 char *sptr,
160 int dlen,
161 struct ip *pip,	  /* IP packet to examine/patch */
162 struct alias_link *link, /* The link to go through (aliased port) */
163 int maxpacketsize  /* The maximum size this packet can grow to (including headers) */)
164 {
165     struct in_addr true_addr;
166     u_short true_port;
167     char ch, delim;
168     int i, state;
169     u_long a1, a2, a3, a4;
170     u_short pt;
171 
172     a1=0; a2=0; a3=0; a4=0; pt=0;
173     delim='|';				/* XXX gcc -Wuninitialized */
174     state=-4;
175     for (i=0; i<dlen; i++)
176     {
177 	ch = sptr[i];
178 	switch (state)
179 	{
180 	case -4: if (ch == 'E') state++; else return; break;
181 	case -3: if (ch == 'P') state++; else return; break;
182 	case -2: if (ch == 'R') state++; else return; break;
183 	case -1: if (ch == 'T') state++; else return; break;
184 
185 	case 0 :
186 	    if (!isspace(ch)) {delim=ch; state++;} break;
187 	case 1 :
188 	    if (ch=='1') /* IPv4 address */ state++; else return; break;
189 	case 2 :
190 	    if (ch==delim) state++; else return; break;
191 	case 3 :
192 	    if (isdigit(ch)) {a1=ch-'0'; state++;} else return; break;
193 	case 4 :
194 	    if (isdigit(ch)) a1=10*a1+ch-'0';
195 	    else if (ch=='.') state++;
196 	    else return;
197 	    break;
198 	case 5 :
199 	    if (isdigit(ch)) {a2=ch-'0'; state++;} else return; break;
200 	case 6 :
201 	    if (isdigit(ch)) a2=10*a2+ch-'0';
202 	    else if (ch=='.') state++;
203 	    else return;
204 	    break;
205 	case 7:
206 	    if (isdigit(ch)) {a3=ch-'0'; state++;} else return; break;
207 	case 8 :
208 	    if (isdigit(ch)) a3=10*a3+ch-'0';
209 	    else if (ch=='.') state++;
210 	    else return;
211 	    break;
212 	case 9 :
213 	    if (isdigit(ch)) {a4=ch-'0'; state++;} else return; break;
214 	case 10:
215 	    if (isdigit(ch)) a4=10*a4+ch-'0';
216 	    else if (ch==delim) state++;
217 	    else return;
218 	    break;
219 	case 11:
220 	    if (isdigit(ch)) {pt=ch-'0'; state++;} else return; break;
221 	case 12:
222 	    if (isdigit(ch)) pt=10*pt+ch-'0';
223 	    else if (ch==delim) state++;
224 	    else return;
225 	    break;
226 	}
227     }
228 
229     if (state == 13)
230     {
231 	true_port = htons(pt);
232 	true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4);
233 	NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize, 1);
234     }
235 }
236 
237 static void
238 NewFtpPortCommand(struct ip *pip,
239                   struct alias_link *link,
240                   struct in_addr true_addr,
241                   u_short true_port,
242                   int maxpacketsize,
243                   int is_eprt)
244 {
245     struct alias_link *ftp_link;
246 
247 /* Establish link to address and port found in PORT command */
248     ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link),
249                              true_port, 0, IPPROTO_TCP);
250 
251     if (ftp_link != NULL)
252     {
253         int slen, hlen, tlen, dlen;
254         struct tcphdr *tc;
255 
256 #ifndef NO_FW_PUNCH
257 /* Punch hole in firewall */
258         PunchFWHole(ftp_link);
259 #endif
260 
261 /* Calculate data length of TCP packet */
262         tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
263         hlen = (pip->ip_hl + tc->th_off) << 2;
264         tlen = ntohs(pip->ip_len);
265         dlen = tlen - hlen;
266 
267 /* Create new PORT command */
268         {
269             char stemp[80];
270             char *sptr;
271             u_short alias_port;
272             u_char *ptr;
273             int a1, a2, a3, a4, p1, p2;
274             struct in_addr alias_address;
275 
276 /* Decompose alias address into quad format */
277             alias_address = GetAliasAddress(link);
278             ptr = (u_char *) &alias_address.s_addr;
279             a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr;
280 
281 	    alias_port = GetAliasPort(ftp_link);
282 
283 	    if (is_eprt) {
284 /* Generate EPRT command string */
285 		sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
286 			a1,a2,a3,a4,ntohs(alias_port));
287 	    } else {
288 /* Decompose alias port into pair format */
289 		ptr = (char *) &alias_port;
290 		p1 = *ptr++; p2=*ptr;
291 
292 /* Generate PORT command string */
293 		sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
294 			a1,a2,a3,a4,p1,p2);
295 	    }
296 
297 /* Save string length for IP header modification */
298             slen = strlen(stemp);
299 
300 /* Copy into IP packet */
301             sptr = (char *) pip; sptr += hlen;
302             strncpy(sptr, stemp, maxpacketsize-hlen);
303         }
304 
305 /* Save information regarding modified seq and ack numbers */
306         {
307             int delta;
308 
309             SetAckModified(link);
310             delta = GetDeltaSeqOut(pip, link);
311             AddSeq(pip, link, delta+slen-dlen);
312         }
313 
314 /* Revise IP header */
315         {
316             u_short new_len;
317 
318             new_len = htons(hlen + slen);
319             DifferentialChecksum(&pip->ip_sum,
320                                  &new_len,
321                                  &pip->ip_len,
322                                  1);
323             pip->ip_len = new_len;
324         }
325 
326 /* Compute TCP checksum for revised packet */
327         tc->th_sum = 0;
328         tc->th_sum = TcpChecksum(pip);
329     }
330     else
331     {
332 #ifdef DEBUG
333         fprintf(stderr,
334         "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
335 #endif
336     }
337 }
338