VirtualBox

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

Last change on this file since 45322 was 39101, checked in by vboxsync, 13 years ago

NAT: warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.8 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# define isspace(ch) RT_C_IS_SPACE(ch)
105# define isdigit(ch) RT_C_IS_DIGIT(ch)
106#endif /* VBOX */
107
108#define FTP_CONTROL_PORT_NUMBER 21
109
110static void
111AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *,
112 int maxpacketsize);
113
114static int
115fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
116{
117
118#ifdef VBOX
119 NOREF(la);
120 NOREF(pip);
121#endif
122 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
123 ah->maxpktsize == 0)
124 return (-1);
125 if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER
126 || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
127 return (0);
128 return (-1);
129}
130
131static int
132protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
133{
134
135 AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
136 return (0);
137}
138
139#ifndef VBOX
140struct proto_handler handlers[] = {
141 {
142 .pri = 80,
143 .dir = OUT,
144 .proto = TCP,
145 .fingerprint = &fingerprint,
146 .protohandler = &protohandler
147 },
148 { EOH }
149};
150#else /* !VBOX */
151#define handlers pData->ftp_module
152#endif /* VBOX */
153
154#ifndef VBOX
155static int
156mod_handler(module_t mod, int type, void *data)
157#else
158static int ftp_alias_handler(PNATState pData, int type);
159
160int
161ftp_alias_load(PNATState pData)
162{
163 return ftp_alias_handler(pData, MOD_LOAD);
164}
165
166int
167ftp_alias_unload(PNATState pData)
168{
169 return ftp_alias_handler(pData, MOD_UNLOAD);
170}
171static int
172ftp_alias_handler(PNATState pData, int type)
173#endif
174{
175 int error;
176#ifdef VBOX
177 if (handlers == NULL)
178 handlers = RTMemAllocZ(2 * sizeof(struct proto_handler));
179 handlers[0].pri = 80;
180 handlers[0].dir = OUT;
181 handlers[0].proto = TCP;
182 handlers[0].fingerprint = &fingerprint;
183 handlers[0].protohandler = &protohandler;
184 handlers[1].pri = EOH;
185#endif /* VBOX */
186
187 switch (type) {
188 case MOD_LOAD:
189 error = 0;
190#ifdef VBOX
191 LibAliasAttachHandlers(pData, handlers);
192#else
193 LibAliasAttachHandlers(handlers);
194#endif
195 break;
196 case MOD_UNLOAD:
197 error = 0;
198#ifdef VBOX
199 LibAliasDetachHandlers(pData, handlers);
200 RTMemFree(handlers);
201 handlers = NULL;
202#else
203 LibAliasDetachHandlers(handlers);
204#endif
205 break;
206 default:
207 error = EINVAL;
208 }
209 return (error);
210}
211
212#ifndef VBOX
213#ifdef _KERNEL
214static
215#endif
216moduledata_t alias_mod = {
217 "alias_ftp", mod_handler, NULL
218};
219#endif /*!VBOX*/
220
221#ifdef _KERNEL
222DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
223MODULE_VERSION(alias_ftp, 1);
224MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1);
225#endif
226
227#define FTP_CONTROL_PORT_NUMBER 21
228#define MAX_MESSAGE_SIZE 128
229
230/* FTP protocol flags. */
231#define WAIT_CRLF 0x01
232
233enum ftp_message_type {
234 FTP_PORT_COMMAND,
235 FTP_EPRT_COMMAND,
236 FTP_227_REPLY,
237 FTP_229_REPLY,
238 FTP_UNKNOWN_MESSAGE
239};
240
241static int ParseFtpPortCommand(struct libalias *la, char *, int);
242static int ParseFtpEprtCommand(struct libalias *la, char *, int);
243static int ParseFtp227Reply(struct libalias *la, char *, int);
244static int ParseFtp229Reply(struct libalias *la, char *, int);
245static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int);
246
247static void
248AliasHandleFtpOut(
249 struct libalias *la,
250 struct ip *pip, /* IP packet to examine/patch */
251 struct alias_link *lnk, /* The link to go through (aliased port) */
252 int maxpacketsize /* The maximum size this packet can grow to
253 (including headers) */ )
254{
255 int hlen, tlen, dlen, pflags;
256 char *sptr;
257 struct tcphdr *tc;
258 int ftp_message_type;
259
260/* Calculate data length of TCP packet */
261 tc = (struct tcphdr *)ip_next(pip);
262 hlen = (pip->ip_hl + tc->th_off) << 2;
263 tlen = ntohs(pip->ip_len);
264 dlen = tlen - hlen;
265
266/* Place string pointer and beginning of data */
267 sptr = (char *)pip;
268 sptr += hlen;
269
270/*
271 * Check that data length is not too long and previous message was
272 * properly terminated with CRLF.
273 */
274 pflags = GetProtocolFlags(lnk);
275 if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) {
276 ftp_message_type = FTP_UNKNOWN_MESSAGE;
277
278 if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
279/*
280 * When aliasing a client, check for the PORT/EPRT command.
281 */
282 if (ParseFtpPortCommand(la, sptr, dlen))
283 ftp_message_type = FTP_PORT_COMMAND;
284 else if (ParseFtpEprtCommand(la, sptr, dlen))
285 ftp_message_type = FTP_EPRT_COMMAND;
286 } else {
287/*
288 * When aliasing a server, check for the 227/229 reply.
289 */
290 if (ParseFtp227Reply(la, sptr, dlen))
291 ftp_message_type = FTP_227_REPLY;
292 else if (ParseFtp229Reply(la, sptr, dlen)) {
293 ftp_message_type = FTP_229_REPLY;
294 la->true_addr.s_addr = pip->ip_src.s_addr;
295 }
296 }
297
298 if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
299 NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type);
300 }
301/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
302
303 if (dlen) { /* only if there's data */
304 sptr = (char *)pip; /* start over at beginning */
305 tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may
306 * have grown */
307 if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
308 pflags &= ~WAIT_CRLF;
309 else
310 pflags |= WAIT_CRLF;
311 SetProtocolFlags(lnk, pflags);
312 }
313}
314
315static int
316ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen)
317{
318 char ch;
319 int i, state;
320 u_int32_t addr;
321 u_short port;
322 u_int8_t octet;
323
324 /* Format: "PORT A,D,D,R,PO,RT". */
325
326 /* Return if data length is too short. */
327 if (dlen < 18)
328 return (0);
329
330 if (strncasecmp("PORT ", sptr, 5))
331 return (0);
332
333 addr = port = octet = 0;
334 state = 0;
335 for (i = 5; i < dlen; i++) {
336 ch = sptr[i];
337 switch (state) {
338 case 0:
339 if (isspace(ch))
340 break;
341 else
342 state++;
343 case 1:
344 case 3:
345 case 5:
346 case 7:
347 case 9:
348 case 11:
349 if (isdigit(ch)) {
350 octet = ch - '0';
351 state++;
352 } else
353 return (0);
354 break;
355 case 2:
356 case 4:
357 case 6:
358 case 8:
359 if (isdigit(ch))
360 octet = 10 * octet + ch - '0';
361 else if (ch == ',') {
362 addr = (addr << 8) + octet;
363 state++;
364 } else
365 return (0);
366 break;
367 case 10:
368 case 12:
369 if (isdigit(ch))
370 octet = 10 * octet + ch - '0';
371 else if (ch == ',' || state == 12) {
372 port = (port << 8) + octet;
373 state++;
374 } else
375 return (0);
376 break;
377 }
378 }
379
380 if (state == 13) {
381 la->true_addr.s_addr = htonl(addr);
382 la->true_port = port;
383 return (1);
384 } else
385 return (0);
386}
387
388static int
389ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen)
390{
391 char ch, delim;
392 int i, state;
393 u_int32_t addr;
394 u_short port;
395 u_int8_t octet;
396
397 /* Format: "EPRT |1|A.D.D.R|PORT|". */
398
399 /* Return if data length is too short. */
400 if (dlen < 18)
401 return (0);
402
403 if (strncasecmp("EPRT ", sptr, 5))
404 return (0);
405
406 addr = port = octet = 0;
407 delim = '|'; /* XXX gcc -Wuninitialized */
408 state = 0;
409 for (i = 5; i < dlen; i++) {
410 ch = sptr[i];
411 switch (state) {
412 case 0:
413 if (!isspace(ch)) {
414 delim = ch;
415 state++;
416 }
417 break;
418 case 1:
419 if (ch == '1') /* IPv4 address */
420 state++;
421 else
422 return (0);
423 break;
424 case 2:
425 if (ch == delim)
426 state++;
427 else
428 return (0);
429 break;
430 case 3:
431 case 5:
432 case 7:
433 case 9:
434 if (isdigit(ch)) {
435 octet = ch - '0';
436 state++;
437 } else
438 return (0);
439 break;
440 case 4:
441 case 6:
442 case 8:
443 case 10:
444 if (isdigit(ch))
445 octet = 10 * octet + ch - '0';
446 else if (ch == '.' || state == 10) {
447 addr = (addr << 8) + octet;
448 state++;
449 } else
450 return (0);
451 break;
452 case 11:
453 if (isdigit(ch)) {
454 port = ch - '0';
455 state++;
456 } else
457 return (0);
458 break;
459 case 12:
460 if (isdigit(ch))
461 port = 10 * port + ch - '0';
462 else if (ch == delim)
463 state++;
464 else
465 return (0);
466 break;
467 }
468 }
469
470 if (state == 13) {
471 la->true_addr.s_addr = htonl(addr);
472 la->true_port = port;
473 return (1);
474 } else
475 return (0);
476}
477
478static int
479ParseFtp227Reply(struct libalias *la, char *sptr, int dlen)
480{
481 char ch;
482 int i, state;
483 u_int32_t addr;
484 u_short port;
485 u_int8_t octet;
486
487 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
488
489 /* Return if data length is too short. */
490 if (dlen < 17)
491 return (0);
492
493 if (strncmp("227 ", sptr, 4))
494 return (0);
495
496 addr = port = octet = 0;
497
498 state = 0;
499 for (i = 4; i < dlen; i++) {
500 ch = sptr[i];
501 switch (state) {
502 case 0:
503 if (ch == '(')
504 state++;
505 break;
506 case 1:
507 case 3:
508 case 5:
509 case 7:
510 case 9:
511 case 11:
512 if (isdigit(ch)) {
513 octet = ch - '0';
514 state++;
515 } else
516 return (0);
517 break;
518 case 2:
519 case 4:
520 case 6:
521 case 8:
522 if (isdigit(ch))
523 octet = 10 * octet + ch - '0';
524 else if (ch == ',') {
525 addr = (addr << 8) + octet;
526 state++;
527 } else
528 return (0);
529 break;
530 case 10:
531 case 12:
532 if (isdigit(ch))
533 octet = 10 * octet + ch - '0';
534 else if (ch == ',' || (state == 12 && ch == ')')) {
535 port = (port << 8) + octet;
536 state++;
537 } else
538 return (0);
539 break;
540 }
541 }
542
543 if (state == 13) {
544 la->true_port = port;
545 la->true_addr.s_addr = htonl(addr);
546 return (1);
547 } else
548 return (0);
549}
550
551static int
552ParseFtp229Reply(struct libalias *la, char *sptr, int dlen)
553{
554 char ch, delim;
555 int i, state;
556 u_short port;
557
558 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
559
560 /* Return if data length is too short. */
561 if (dlen < 11)
562 return (0);
563
564 if (strncmp("229 ", sptr, 4))
565 return (0);
566
567 port = 0;
568 delim = '|'; /* XXX gcc -Wuninitialized */
569
570 state = 0;
571 for (i = 4; i < dlen; i++) {
572 ch = sptr[i];
573 switch (state) {
574 case 0:
575 if (ch == '(')
576 state++;
577 break;
578 case 1:
579 delim = ch;
580 state++;
581 break;
582 case 2:
583 case 3:
584 if (ch == delim)
585 state++;
586 else
587 return (0);
588 break;
589 case 4:
590 if (isdigit(ch)) {
591 port = ch - '0';
592 state++;
593 } else
594 return (0);
595 break;
596 case 5:
597 if (isdigit(ch))
598 port = 10 * port + ch - '0';
599 else if (ch == delim)
600 state++;
601 else
602 return (0);
603 break;
604 case 6:
605 if (ch == ')')
606 state++;
607 else
608 return (0);
609 break;
610 }
611 }
612
613 if (state == 7) {
614 la->true_port = port;
615 return (1);
616 } else
617 return (0);
618}
619
620static void
621NewFtpMessage(struct libalias *la, struct ip *pip,
622 struct alias_link *lnk,
623 int maxpacketsize,
624 int ftp_message_type)
625{
626 struct alias_link *ftp_lnk;
627
628/* Security checks. */
629 if (pip->ip_src.s_addr != la->true_addr.s_addr)
630 return;
631
632 if (la->true_port < IPPORT_RESERVED)
633 return;
634
635/* Establish link to address and port found in FTP control message. */
636 ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk),
637 htons(la->true_port), 0, IPPROTO_TCP, 1);
638
639 if (ftp_lnk != NULL) {
640 int slen, hlen, tlen, dlen;
641 struct tcphdr *tc;
642
643#ifndef NO_FW_PUNCH
644 /* Punch hole in firewall */
645 PunchFWHole(ftp_lnk);
646#endif
647
648/* Calculate data length of TCP packet */
649 tc = (struct tcphdr *)ip_next(pip);
650 hlen = (pip->ip_hl + tc->th_off) << 2;
651 tlen = ntohs(pip->ip_len);
652 dlen = tlen - hlen;
653
654/* Create new FTP message. */
655 {
656 char stemp[MAX_MESSAGE_SIZE + 1];
657 char *sptr;
658 u_short alias_port;
659 u_char *ptr;
660 int a1, a2, a3, a4, p1, p2;
661 struct in_addr alias_address;
662
663/* Decompose alias address into quad format */
664 alias_address = GetAliasAddress(lnk);
665 ptr = (u_char *) & alias_address.s_addr;
666 a1 = *ptr++;
667 a2 = *ptr++;
668 a3 = *ptr++;
669 a4 = *ptr;
670
671 alias_port = GetAliasPort(ftp_lnk);
672
673/* Prepare new command */
674 switch (ftp_message_type) {
675 case FTP_PORT_COMMAND:
676 case FTP_227_REPLY:
677 /* Decompose alias port into pair format. */
678 ptr = (u_char *)&alias_port;
679 p1 = *ptr++;
680 p2 = *ptr;
681
682 if (ftp_message_type == FTP_PORT_COMMAND) {
683 /* Generate PORT command string. */
684#ifndef VBOX
685 sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
686 a1, a2, a3, a4, p1, p2);
687#else
688 RTStrPrintf(stemp, sizeof(stemp), "PORT %d,%d,%d,%d,%d,%d\r\n",
689 a1, a2, a3, a4, p1, p2);
690#endif
691 } else {
692 /* Generate 227 reply string. */
693#ifndef VBOX
694 sprintf(stemp,
695 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
696 a1, a2, a3, a4, p1, p2);
697#else
698 RTStrPrintf(stemp, sizeof(stemp),
699 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
700 a1, a2, a3, a4, p1, p2);
701#endif
702 }
703 break;
704 case FTP_EPRT_COMMAND:
705 /* Generate EPRT command string. */
706#ifndef VBOX
707 sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
708 a1, a2, a3, a4, ntohs(alias_port));
709#else
710 RTStrPrintf(stemp, sizeof(stemp), "EPRT |1|%d.%d.%d.%d|%d|\r\n",
711 a1, a2, a3, a4, ntohs(alias_port));
712#endif
713 break;
714 case FTP_229_REPLY:
715 /* Generate 229 reply string. */
716#ifndef VBOX
717 sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
718 ntohs(alias_port));
719#else
720 RTStrPrintf(stemp, sizeof(stemp), "229 Entering Extended Passive Mode (|||%d|)\r\n",
721 ntohs(alias_port));
722#endif
723 break;
724 }
725
726/* Save string length for IP header modification */
727 slen = strlen(stemp);
728
729/* Copy modified buffer into IP packet. */
730 sptr = (char *)pip;
731 sptr += hlen;
732 strncpy(sptr, stemp, maxpacketsize - hlen);
733 }
734
735/* Save information regarding modified seq and ack numbers */
736 {
737 int delta;
738
739 SetAckModified(lnk);
740 delta = GetDeltaSeqOut(pip, lnk);
741 AddSeq(pip, lnk, delta + slen - dlen);
742 }
743
744/* Revise IP header */
745 {
746 u_short new_len;
747
748 new_len = htons(hlen + slen);
749 DifferentialChecksum(&pip->ip_sum,
750 &new_len,
751 &pip->ip_len,
752 1);
753 pip->ip_len = new_len;
754 }
755
756/* Compute TCP checksum for revised packet */
757 tc->th_sum = 0;
758#ifdef _KERNEL
759 tc->th_x2 = 1;
760#else
761 tc->th_sum = TcpChecksum(pip);
762#endif
763 } else {
764#ifdef LIBALIAS_DEBUG
765 fprintf(stderr,
766 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
767#endif
768 }
769}
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