VirtualBox

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

Last change on this file since 103068 was 69046, checked in by vboxsync, 7 years ago

Global: replace fall-through comments with RT_FALL_THRU().
bugref:8192: gcc warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/*-
2 * Copyright (c) 2001 Charles Mott <cm@linktel.net>
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 = (u_int)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 RT_FALL_THRU();
344 case 1:
345 case 3:
346 case 5:
347 case 7:
348 case 9:
349 case 11:
350 if (isdigit(ch)) {
351 octet = ch - '0';
352 state++;
353 } else
354 return (0);
355 break;
356 case 2:
357 case 4:
358 case 6:
359 case 8:
360 if (isdigit(ch))
361 octet = 10 * octet + ch - '0';
362 else if (ch == ',') {
363 addr = (addr << 8) + octet;
364 state++;
365 } else
366 return (0);
367 break;
368 case 10:
369 case 12:
370 if (isdigit(ch))
371 octet = 10 * octet + ch - '0';
372 else if (ch == ',' || state == 12) {
373 port = (port << 8) + octet;
374 state++;
375 } else
376 return (0);
377 break;
378 }
379 }
380
381 if (state == 13) {
382 la->true_addr.s_addr = htonl(addr);
383 la->true_port = port;
384 return (1);
385 } else
386 return (0);
387}
388
389static int
390ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen)
391{
392 char ch, delim;
393 int i, state;
394 u_int32_t addr;
395 u_short port;
396 u_int8_t octet;
397
398 /* Format: "EPRT |1|A.D.D.R|PORT|". */
399
400 /* Return if data length is too short. */
401 if (dlen < 18)
402 return (0);
403
404 if (strncasecmp("EPRT ", sptr, 5))
405 return (0);
406
407 addr = port = octet = 0;
408 delim = '|'; /* XXX gcc -Wuninitialized */
409 state = 0;
410 for (i = 5; i < dlen; i++) {
411 ch = sptr[i];
412 switch (state) {
413 case 0:
414 if (!isspace(ch)) {
415 delim = ch;
416 state++;
417 }
418 break;
419 case 1:
420 if (ch == '1') /* IPv4 address */
421 state++;
422 else
423 return (0);
424 break;
425 case 2:
426 if (ch == delim)
427 state++;
428 else
429 return (0);
430 break;
431 case 3:
432 case 5:
433 case 7:
434 case 9:
435 if (isdigit(ch)) {
436 octet = ch - '0';
437 state++;
438 } else
439 return (0);
440 break;
441 case 4:
442 case 6:
443 case 8:
444 case 10:
445 if (isdigit(ch))
446 octet = 10 * octet + ch - '0';
447 else if (ch == '.' || state == 10) {
448 addr = (addr << 8) + octet;
449 state++;
450 } else
451 return (0);
452 break;
453 case 11:
454 if (isdigit(ch)) {
455 port = ch - '0';
456 state++;
457 } else
458 return (0);
459 break;
460 case 12:
461 if (isdigit(ch))
462 port = 10 * port + ch - '0';
463 else if (ch == delim)
464 state++;
465 else
466 return (0);
467 break;
468 }
469 }
470
471 if (state == 13) {
472 la->true_addr.s_addr = htonl(addr);
473 la->true_port = port;
474 return (1);
475 } else
476 return (0);
477}
478
479static int
480ParseFtp227Reply(struct libalias *la, char *sptr, int dlen)
481{
482 char ch;
483 int i, state;
484 u_int32_t addr;
485 u_short port;
486 u_int8_t octet;
487
488 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
489
490 /* Return if data length is too short. */
491 if (dlen < 17)
492 return (0);
493
494 if (strncmp("227 ", sptr, 4))
495 return (0);
496
497 addr = port = octet = 0;
498
499 state = 0;
500 for (i = 4; i < dlen; i++) {
501 ch = sptr[i];
502 switch (state) {
503 case 0:
504 if (ch == '(')
505 state++;
506 break;
507 case 1:
508 case 3:
509 case 5:
510 case 7:
511 case 9:
512 case 11:
513 if (isdigit(ch)) {
514 octet = ch - '0';
515 state++;
516 } else
517 return (0);
518 break;
519 case 2:
520 case 4:
521 case 6:
522 case 8:
523 if (isdigit(ch))
524 octet = 10 * octet + ch - '0';
525 else if (ch == ',') {
526 addr = (addr << 8) + octet;
527 state++;
528 } else
529 return (0);
530 break;
531 case 10:
532 case 12:
533 if (isdigit(ch))
534 octet = 10 * octet + ch - '0';
535 else if (ch == ',' || (state == 12 && ch == ')')) {
536 port = (port << 8) + octet;
537 state++;
538 } else
539 return (0);
540 break;
541 }
542 }
543
544 if (state == 13) {
545 if (addr != INADDR_LOOPBACK)
546 la->true_addr.s_addr = htonl(addr);
547 else
548 la->true_addr.s_addr = la->pData->alias_addr.s_addr;
549 la->true_port = port;
550 return (1);
551 } else
552 return (0);
553}
554
555static int
556ParseFtp229Reply(struct libalias *la, char *sptr, int dlen)
557{
558 char ch, delim;
559 int i, state;
560 u_short port;
561
562 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
563
564 /* Return if data length is too short. */
565 if (dlen < 11)
566 return (0);
567
568 if (strncmp("229 ", sptr, 4))
569 return (0);
570
571 port = 0;
572 delim = '|'; /* XXX gcc -Wuninitialized */
573
574 state = 0;
575 for (i = 4; i < dlen; i++) {
576 ch = sptr[i];
577 switch (state) {
578 case 0:
579 if (ch == '(')
580 state++;
581 break;
582 case 1:
583 delim = ch;
584 state++;
585 break;
586 case 2:
587 case 3:
588 if (ch == delim)
589 state++;
590 else
591 return (0);
592 break;
593 case 4:
594 if (isdigit(ch)) {
595 port = ch - '0';
596 state++;
597 } else
598 return (0);
599 break;
600 case 5:
601 if (isdigit(ch))
602 port = 10 * port + ch - '0';
603 else if (ch == delim)
604 state++;
605 else
606 return (0);
607 break;
608 case 6:
609 if (ch == ')')
610 state++;
611 else
612 return (0);
613 break;
614 }
615 }
616
617 if (state == 7) {
618 la->true_port = port;
619 return (1);
620 } else
621 return (0);
622}
623
624static void
625NewFtpMessage(struct libalias *la, struct ip *pip,
626 struct alias_link *lnk,
627 int maxpacketsize,
628 int ftp_message_type)
629{
630 struct alias_link *ftp_lnk;
631
632/* Security checks. */
633 if (pip->ip_src.s_addr != la->true_addr.s_addr)
634 return;
635
636 if (la->true_port < IPPORT_RESERVED)
637 return;
638
639/* Establish link to address and port found in FTP control message. */
640 ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk),
641 htons(la->true_port), 0, IPPROTO_TCP, 1);
642
643 if (ftp_lnk != NULL) {
644 int slen, hlen, tlen, dlen;
645 struct tcphdr *tc;
646
647#ifndef NO_FW_PUNCH
648 /* Punch hole in firewall */
649 PunchFWHole(ftp_lnk);
650#endif
651
652/* Calculate data length of TCP packet */
653 tc = (struct tcphdr *)ip_next(pip);
654 hlen = (pip->ip_hl + tc->th_off) << 2;
655 tlen = ntohs(pip->ip_len);
656 dlen = tlen - hlen;
657
658/* Create new FTP message. */
659 {
660 char stemp[MAX_MESSAGE_SIZE + 1];
661 char *sptr;
662 u_short alias_port;
663 u_char *ptr;
664 int a1, a2, a3, a4, p1, p2;
665 struct in_addr alias_address;
666
667/* Decompose alias address into quad format */
668 alias_address = GetAliasAddress(lnk);
669 ptr = (u_char *) & alias_address.s_addr;
670 a1 = *ptr++;
671 a2 = *ptr++;
672 a3 = *ptr++;
673 a4 = *ptr;
674
675 alias_port = GetAliasPort(ftp_lnk);
676
677/* Prepare new command */
678 switch (ftp_message_type) {
679 case FTP_PORT_COMMAND:
680 case FTP_227_REPLY:
681 /* Decompose alias port into pair format. */
682 ptr = (u_char *)&alias_port;
683 p1 = *ptr++;
684 p2 = *ptr;
685
686 if (ftp_message_type == FTP_PORT_COMMAND) {
687 /* Generate PORT command string. */
688#ifndef VBOX
689 sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
690 a1, a2, a3, a4, p1, p2);
691#else
692 RTStrPrintf(stemp, sizeof(stemp), "PORT %d,%d,%d,%d,%d,%d\r\n",
693 a1, a2, a3, a4, p1, p2);
694#endif
695 } else {
696 /* Generate 227 reply string. */
697#ifndef VBOX
698 sprintf(stemp,
699 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
700 a1, a2, a3, a4, p1, p2);
701#else
702 RTStrPrintf(stemp, sizeof(stemp),
703 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
704 a1, a2, a3, a4, p1, p2);
705#endif
706 }
707 break;
708 case FTP_EPRT_COMMAND:
709 /* Generate EPRT command string. */
710#ifndef VBOX
711 sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
712 a1, a2, a3, a4, ntohs(alias_port));
713#else
714 RTStrPrintf(stemp, sizeof(stemp), "EPRT |1|%d.%d.%d.%d|%d|\r\n",
715 a1, a2, a3, a4, ntohs(alias_port));
716#endif
717 break;
718 case FTP_229_REPLY:
719 /* Generate 229 reply string. */
720#ifndef VBOX
721 sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
722 ntohs(alias_port));
723#else
724 RTStrPrintf(stemp, sizeof(stemp), "229 Entering Extended Passive Mode (|||%d|)\r\n",
725 ntohs(alias_port));
726#endif
727 break;
728 }
729
730/* Save string length for IP header modification */
731 slen = (int)strlen(stemp);
732
733/* Copy modified buffer into IP packet. */
734 sptr = (char *)pip;
735 sptr += hlen;
736 strncpy(sptr, stemp, maxpacketsize - hlen);
737 }
738
739/* Save information regarding modified seq and ack numbers */
740 {
741 int delta;
742
743 SetAckModified(lnk);
744 delta = GetDeltaSeqOut(pip, lnk);
745 AddSeq(pip, lnk, delta + slen - dlen);
746 }
747
748/* Revise IP header */
749 {
750 u_short new_len;
751
752 new_len = htons(hlen + slen);
753 DifferentialChecksum(&pip->ip_sum,
754 &new_len,
755 &pip->ip_len,
756 1);
757 pip->ip_len = new_len;
758 }
759
760/* Compute TCP checksum for revised packet */
761 tc->th_sum = 0;
762#ifdef _KERNEL
763 tc->th_x2 = 1;
764#else
765 tc->th_sum = TcpChecksum(pip);
766#endif
767 } else {
768#ifdef LIBALIAS_DEBUG
769 fprintf(stderr,
770 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
771#endif
772 }
773}
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