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