VirtualBox

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

Last change on this file since 22494 was 22452, checked in by vboxsync, 16 years ago

NAT/libalias: don't alloc to much

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