VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/libalias/alias_ftp.c@ 22315

Last change on this file since 22315 was 21864, checked in by vboxsync, 16 years ago

NAT: globals

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1/*-
2 * Copyright (c) 2001 Charles Mott <[email protected]>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#ifndef VBOX
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_ftp.c,v 1.29.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $");
30
31/*
32 Alias_ftp.c performs special processing for FTP sessions under
33 TCP. Specifically, when a PORT/EPRT command from the client
34 side or 227/229 reply from the server is sent, it is intercepted
35 and modified. The address is changed to the gateway machine
36 and an aliasing port is used.
37
38 For this routine to work, the message must fit entirely into a
39 single TCP packet. This is typically the case, but exceptions
40 can easily be envisioned under the actual specifications.
41
42 Probably the most troubling aspect of the approach taken here is
43 that the new message will typically be a different length, and
44 this causes a certain amount of bookkeeping to keep track of the
45 changes of sequence and acknowledgment numbers, since the client
46 machine is totally unaware of the modification to the TCP stream.
47
48
49 References: RFC 959, RFC 2428.
50
51 Initial version: August, 1996 (cjm)
52
53 Version 1.6
54 Brian Somers and Martin Renters identified an IP checksum
55 error for modified IP packets.
56
57 Version 1.7: January 9, 1996 (cjm)
58 Differential checksum computation for change
59 in IP packet length.
60
61 Version 2.1: May, 1997 (cjm)
62 Very minor changes to conform with
63 local/global/function naming conventions
64 within the packet aliasing module.
65
66 Version 3.1: May, 2000 (eds)
67 Add support for passive mode, alias the 227 replies.
68
69 See HISTORY file for record of revisions.
70*/
71
72/* Includes */
73#ifdef _KERNEL
74#include <sys/param.h>
75#include <sys/ctype.h>
76#include <sys/systm.h>
77#include <sys/kernel.h>
78#include <sys/module.h>
79#else
80#include <errno.h>
81#include <sys/types.h>
82#include <stdio.h>
83#include <string.h>
84#endif
85
86#include <netinet/in_systm.h>
87#include <netinet/in.h>
88#include <netinet/ip.h>
89#include <netinet/tcp.h>
90
91#ifdef _KERNEL
92#include <netinet/libalias/alias.h>
93#include <netinet/libalias/alias_local.h>
94#include <netinet/libalias/alias_mod.h>
95#else
96#include "alias_local.h"
97#include "alias_mod.h"
98#endif
99#else /* VBOX */
100# include <iprt/ctype.h>
101# include <slirp.h>
102# include "alias_local.h"
103# include "alias_mod.h"
104#endif /* VBOX */
105
106#define FTP_CONTROL_PORT_NUMBER 21
107
108static void
109AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *,
110 int maxpacketsize);
111
112static int
113fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
114{
115
116 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
117 ah->maxpktsize == 0)
118 return (-1);
119 if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER
120 || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
121 return (0);
122 return (-1);
123}
124
125static int
126protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
127{
128
129 AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
130 return (0);
131}
132
133#ifndef VBOX
134struct proto_handler handlers[] = {
135 {
136 .pri = 80,
137 .dir = OUT,
138 .proto = TCP,
139 .fingerprint = &fingerprint,
140 .protohandler = &protohandler
141 },
142 { EOH }
143};
144#else /* !VBOX */
145static struct proto_handler handlers[2];
146#endif /* VBOX */
147
148#ifndef VBOX
149static int
150mod_handler(module_t mod, int type, void *data)
151#else
152static int ftp_alias_handler(PNATState pData, int type);
153
154int
155ftp_alias_load(PNATState pData)
156{
157 return ftp_alias_handler(pData, MOD_LOAD);
158}
159
160int
161ftp_alias_unload(PNATState pData)
162{
163 return ftp_alias_handler(pData, MOD_UNLOAD);
164}
165static int
166ftp_alias_handler(PNATState pData, int type)
167#endif
168{
169 int error;
170#ifdef VBOX
171 handlers[0].pri = 80;
172 handlers[0].dir = OUT;
173 handlers[0].proto = TCP;
174 handlers[0].fingerprint = &fingerprint;
175 handlers[0].protohandler = &protohandler;
176 handlers[1].pri = EOH;
177#endif /* VBOX */
178
179 switch (type) {
180 case MOD_LOAD:
181 error = 0;
182#ifdef VBOX
183 LibAliasAttachHandlers(pData, handlers);
184#else
185 LibAliasAttachHandlers(handlers);
186#endif
187 break;
188 case MOD_UNLOAD:
189 error = 0;
190#ifdef VBOX
191 LibAliasDetachHandlers(pData, handlers);
192#else
193 LibAliasDetachHandlers(handlers);
194#endif
195 break;
196 default:
197 error = EINVAL;
198 }
199 return (error);
200}
201
202#ifndef VBOX
203#ifdef _KERNEL
204static
205#endif
206moduledata_t alias_mod = {
207 "alias_ftp", mod_handler, NULL
208};
209#endif /*!VBOX*/
210
211#ifdef _KERNEL
212DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
213MODULE_VERSION(alias_ftp, 1);
214MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1);
215#endif
216
217#define FTP_CONTROL_PORT_NUMBER 21
218#define MAX_MESSAGE_SIZE 128
219
220/* FTP protocol flags. */
221#define WAIT_CRLF 0x01
222
223enum ftp_message_type {
224 FTP_PORT_COMMAND,
225 FTP_EPRT_COMMAND,
226 FTP_227_REPLY,
227 FTP_229_REPLY,
228 FTP_UNKNOWN_MESSAGE
229};
230
231static int ParseFtpPortCommand(struct libalias *la, char *, int);
232static int ParseFtpEprtCommand(struct libalias *la, char *, int);
233static int ParseFtp227Reply(struct libalias *la, char *, int);
234static int ParseFtp229Reply(struct libalias *la, char *, int);
235static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int);
236
237static void
238AliasHandleFtpOut(
239 struct libalias *la,
240 struct ip *pip, /* IP packet to examine/patch */
241 struct alias_link *lnk, /* The link to go through (aliased port) */
242 int maxpacketsize /* The maximum size this packet can grow to
243 (including headers) */ )
244{
245 int hlen, tlen, dlen, pflags;
246 char *sptr;
247 struct tcphdr *tc;
248 int ftp_message_type;
249
250/* Calculate data length of TCP packet */
251 tc = (struct tcphdr *)ip_next(pip);
252 hlen = (pip->ip_hl + tc->th_off) << 2;
253 tlen = ntohs(pip->ip_len);
254 dlen = tlen - hlen;
255
256/* Place string pointer and beginning of data */
257 sptr = (char *)pip;
258 sptr += hlen;
259
260/*
261 * Check that data length is not too long and previous message was
262 * properly terminated with CRLF.
263 */
264 pflags = GetProtocolFlags(lnk);
265 if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) {
266 ftp_message_type = FTP_UNKNOWN_MESSAGE;
267
268 if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
269/*
270 * When aliasing a client, check for the PORT/EPRT command.
271 */
272 if (ParseFtpPortCommand(la, sptr, dlen))
273 ftp_message_type = FTP_PORT_COMMAND;
274 else if (ParseFtpEprtCommand(la, sptr, dlen))
275 ftp_message_type = FTP_EPRT_COMMAND;
276 } else {
277/*
278 * When aliasing a server, check for the 227/229 reply.
279 */
280 if (ParseFtp227Reply(la, sptr, dlen))
281 ftp_message_type = FTP_227_REPLY;
282 else if (ParseFtp229Reply(la, sptr, dlen)) {
283 ftp_message_type = FTP_229_REPLY;
284 la->true_addr.s_addr = pip->ip_src.s_addr;
285 }
286 }
287
288 if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
289 NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type);
290 }
291/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
292
293 if (dlen) { /* only if there's data */
294 sptr = (char *)pip; /* start over at beginning */
295 tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may
296 * have grown */
297 if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
298 pflags &= ~WAIT_CRLF;
299 else
300 pflags |= WAIT_CRLF;
301 SetProtocolFlags(lnk, pflags);
302 }
303}
304
305static int
306ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen)
307{
308 char ch;
309 int i, state;
310 u_int32_t addr;
311 u_short port;
312 u_int8_t octet;
313
314 /* Format: "PORT A,D,D,R,PO,RT". */
315
316 /* Return if data length is too short. */
317 if (dlen < 18)
318 return (0);
319
320 if (strncasecmp("PORT ", sptr, 5))
321 return (0);
322
323 addr = port = octet = 0;
324 state = 0;
325 for (i = 5; i < dlen; i++) {
326 ch = sptr[i];
327 switch (state) {
328 case 0:
329 if (isspace(ch))
330 break;
331 else
332 state++;
333 case 1:
334 case 3:
335 case 5:
336 case 7:
337 case 9:
338 case 11:
339 if (isdigit(ch)) {
340 octet = ch - '0';
341 state++;
342 } else
343 return (0);
344 break;
345 case 2:
346 case 4:
347 case 6:
348 case 8:
349 if (isdigit(ch))
350 octet = 10 * octet + ch - '0';
351 else if (ch == ',') {
352 addr = (addr << 8) + octet;
353 state++;
354 } else
355 return (0);
356 break;
357 case 10:
358 case 12:
359 if (isdigit(ch))
360 octet = 10 * octet + ch - '0';
361 else if (ch == ',' || state == 12) {
362 port = (port << 8) + octet;
363 state++;
364 } else
365 return (0);
366 break;
367 }
368 }
369
370 if (state == 13) {
371 la->true_addr.s_addr = htonl(addr);
372 la->true_port = port;
373 return (1);
374 } else
375 return (0);
376}
377
378static int
379ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen)
380{
381 char ch, delim;
382 int i, state;
383 u_int32_t addr;
384 u_short port;
385 u_int8_t octet;
386
387 /* Format: "EPRT |1|A.D.D.R|PORT|". */
388
389 /* Return if data length is too short. */
390 if (dlen < 18)
391 return (0);
392
393 if (strncasecmp("EPRT ", sptr, 5))
394 return (0);
395
396 addr = port = octet = 0;
397 delim = '|'; /* XXX gcc -Wuninitialized */
398 state = 0;
399 for (i = 5; i < dlen; i++) {
400 ch = sptr[i];
401 switch (state) {
402 case 0:
403 if (!isspace(ch)) {
404 delim = ch;
405 state++;
406 }
407 break;
408 case 1:
409 if (ch == '1') /* IPv4 address */
410 state++;
411 else
412 return (0);
413 break;
414 case 2:
415 if (ch == delim)
416 state++;
417 else
418 return (0);
419 break;
420 case 3:
421 case 5:
422 case 7:
423 case 9:
424 if (isdigit(ch)) {
425 octet = ch - '0';
426 state++;
427 } else
428 return (0);
429 break;
430 case 4:
431 case 6:
432 case 8:
433 case 10:
434 if (isdigit(ch))
435 octet = 10 * octet + ch - '0';
436 else if (ch == '.' || state == 10) {
437 addr = (addr << 8) + octet;
438 state++;
439 } else
440 return (0);
441 break;
442 case 11:
443 if (isdigit(ch)) {
444 port = ch - '0';
445 state++;
446 } else
447 return (0);
448 break;
449 case 12:
450 if (isdigit(ch))
451 port = 10 * port + ch - '0';
452 else if (ch == delim)
453 state++;
454 else
455 return (0);
456 break;
457 }
458 }
459
460 if (state == 13) {
461 la->true_addr.s_addr = htonl(addr);
462 la->true_port = port;
463 return (1);
464 } else
465 return (0);
466}
467
468static int
469ParseFtp227Reply(struct libalias *la, char *sptr, int dlen)
470{
471 char ch;
472 int i, state;
473 u_int32_t addr;
474 u_short port;
475 u_int8_t octet;
476
477 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
478
479 /* Return if data length is too short. */
480 if (dlen < 17)
481 return (0);
482
483 if (strncmp("227 ", sptr, 4))
484 return (0);
485
486 addr = port = octet = 0;
487
488 state = 0;
489 for (i = 4; i < dlen; i++) {
490 ch = sptr[i];
491 switch (state) {
492 case 0:
493 if (ch == '(')
494 state++;
495 break;
496 case 1:
497 case 3:
498 case 5:
499 case 7:
500 case 9:
501 case 11:
502 if (isdigit(ch)) {
503 octet = ch - '0';
504 state++;
505 } else
506 return (0);
507 break;
508 case 2:
509 case 4:
510 case 6:
511 case 8:
512 if (isdigit(ch))
513 octet = 10 * octet + ch - '0';
514 else if (ch == ',') {
515 addr = (addr << 8) + octet;
516 state++;
517 } else
518 return (0);
519 break;
520 case 10:
521 case 12:
522 if (isdigit(ch))
523 octet = 10 * octet + ch - '0';
524 else if (ch == ',' || (state == 12 && ch == ')')) {
525 port = (port << 8) + octet;
526 state++;
527 } else
528 return (0);
529 break;
530 }
531 }
532
533 if (state == 13) {
534 la->true_port = port;
535 la->true_addr.s_addr = htonl(addr);
536 return (1);
537 } else
538 return (0);
539}
540
541static int
542ParseFtp229Reply(struct libalias *la, char *sptr, int dlen)
543{
544 char ch, delim;
545 int i, state;
546 u_short port;
547
548 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
549
550 /* Return if data length is too short. */
551 if (dlen < 11)
552 return (0);
553
554 if (strncmp("229 ", sptr, 4))
555 return (0);
556
557 port = 0;
558 delim = '|'; /* XXX gcc -Wuninitialized */
559
560 state = 0;
561 for (i = 4; i < dlen; i++) {
562 ch = sptr[i];
563 switch (state) {
564 case 0:
565 if (ch == '(')
566 state++;
567 break;
568 case 1:
569 delim = ch;
570 state++;
571 break;
572 case 2:
573 case 3:
574 if (ch == delim)
575 state++;
576 else
577 return (0);
578 break;
579 case 4:
580 if (isdigit(ch)) {
581 port = ch - '0';
582 state++;
583 } else
584 return (0);
585 break;
586 case 5:
587 if (isdigit(ch))
588 port = 10 * port + ch - '0';
589 else if (ch == delim)
590 state++;
591 else
592 return (0);
593 break;
594 case 6:
595 if (ch == ')')
596 state++;
597 else
598 return (0);
599 break;
600 }
601 }
602
603 if (state == 7) {
604 la->true_port = port;
605 return (1);
606 } else
607 return (0);
608}
609
610static void
611NewFtpMessage(struct libalias *la, struct ip *pip,
612 struct alias_link *lnk,
613 int maxpacketsize,
614 int ftp_message_type)
615{
616 struct alias_link *ftp_lnk;
617
618/* Security checks. */
619 if (pip->ip_src.s_addr != la->true_addr.s_addr)
620 return;
621
622 if (la->true_port < IPPORT_RESERVED)
623 return;
624
625/* Establish link to address and port found in FTP control message. */
626 ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk),
627 htons(la->true_port), 0, IPPROTO_TCP, 1);
628
629 if (ftp_lnk != NULL) {
630 int slen, hlen, tlen, dlen;
631 struct tcphdr *tc;
632
633#ifndef NO_FW_PUNCH
634 /* Punch hole in firewall */
635 PunchFWHole(ftp_lnk);
636#endif
637
638/* Calculate data length of TCP packet */
639 tc = (struct tcphdr *)ip_next(pip);
640 hlen = (pip->ip_hl + tc->th_off) << 2;
641 tlen = ntohs(pip->ip_len);
642 dlen = tlen - hlen;
643
644/* Create new FTP message. */
645 {
646 char stemp[MAX_MESSAGE_SIZE + 1];
647 char *sptr;
648 u_short alias_port;
649 u_char *ptr;
650 int a1, a2, a3, a4, p1, p2;
651 struct in_addr alias_address;
652
653/* Decompose alias address into quad format */
654 alias_address = GetAliasAddress(lnk);
655 ptr = (u_char *) & alias_address.s_addr;
656 a1 = *ptr++;
657 a2 = *ptr++;
658 a3 = *ptr++;
659 a4 = *ptr;
660
661 alias_port = GetAliasPort(ftp_lnk);
662
663/* Prepare new command */
664 switch (ftp_message_type) {
665 case FTP_PORT_COMMAND:
666 case FTP_227_REPLY:
667 /* Decompose alias port into pair format. */
668 ptr = (u_char *)&alias_port;
669 p1 = *ptr++;
670 p2 = *ptr;
671
672 if (ftp_message_type == FTP_PORT_COMMAND) {
673 /* Generate PORT command string. */
674 sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
675 a1, a2, a3, a4, p1, p2);
676 } else {
677 /* Generate 227 reply string. */
678 sprintf(stemp,
679 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
680 a1, a2, a3, a4, p1, p2);
681 }
682 break;
683 case FTP_EPRT_COMMAND:
684 /* Generate EPRT command string. */
685 sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
686 a1, a2, a3, a4, ntohs(alias_port));
687 break;
688 case FTP_229_REPLY:
689 /* Generate 229 reply string. */
690 sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
691 ntohs(alias_port));
692 break;
693 }
694
695/* Save string length for IP header modification */
696 slen = strlen(stemp);
697
698/* Copy modified buffer into IP packet. */
699 sptr = (char *)pip;
700 sptr += hlen;
701 strncpy(sptr, stemp, maxpacketsize - hlen);
702 }
703
704/* Save information regarding modified seq and ack numbers */
705 {
706 int delta;
707
708 SetAckModified(lnk);
709 delta = GetDeltaSeqOut(pip, lnk);
710 AddSeq(pip, lnk, delta + slen - dlen);
711 }
712
713/* Revise IP header */
714 {
715 u_short new_len;
716
717 new_len = htons(hlen + slen);
718 DifferentialChecksum(&pip->ip_sum,
719 &new_len,
720 &pip->ip_len,
721 1);
722 pip->ip_len = new_len;
723 }
724
725/* Compute TCP checksum for revised packet */
726 tc->th_sum = 0;
727#ifdef _KERNEL
728 tc->th_x2 = 1;
729#else
730 tc->th_sum = TcpChecksum(pip);
731#endif
732 } else {
733#ifdef LIBALIAS_DEBUG
734 fprintf(stderr,
735 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
736#endif
737 }
738}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette