]>
Commit | Line | Data |
---|---|---|
7ba0088d A |
1 | /* |
2 | * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
921c0aec A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
7ba0088d | 11 | * |
921c0aec A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
7ba0088d A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
921c0aec A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
7ba0088d A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /*- | |
23 | * Copyright (c) 2001 Charles Mott <cmott@scientech.com> | |
24 | * All rights reserved. | |
25 | * | |
26 | * Redistribution and use in source and binary forms, with or without | |
27 | * modification, are permitted provided that the following conditions | |
28 | * are met: | |
29 | * 1. Redistributions of source code must retain the above copyright | |
30 | * notice, this list of conditions and the following disclaimer. | |
31 | * 2. Redistributions in binary form must reproduce the above copyright | |
32 | * notice, this list of conditions and the following disclaimer in the | |
33 | * documentation and/or other materials provided with the distribution. | |
34 | * | |
35 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
36 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
37 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
38 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
39 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
40 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
41 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
42 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
43 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
44 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
45 | * SUCH DAMAGE. | |
46 | * | |
47 | * Based upon: | |
48 | * $FreeBSD: src/lib/libalias/alias_ftp.c,v 1.5.2.4 2001/08/21 03:50:25 brian Exp $ | |
49 | */ | |
50 | ||
b7080c8e A |
51 | /* |
52 | Alias_ftp.c performs special processing for FTP sessions under | |
7ba0088d A |
53 | TCP. Specifically, when a PORT/EPRT command from the client |
54 | side or 227/229 reply from the server is sent, it is intercepted | |
55 | and modified. The address is changed to the gateway machine | |
56 | and an aliasing port is used. | |
b7080c8e | 57 | |
7ba0088d A |
58 | For this routine to work, the message must fit entirely into a |
59 | single TCP packet. This is typically the case, but exceptions | |
b7080c8e A |
60 | can easily be envisioned under the actual specifications. |
61 | ||
62 | Probably the most troubling aspect of the approach taken here is | |
7ba0088d | 63 | that the new message will typically be a different length, and |
b7080c8e A |
64 | this causes a certain amount of bookkeeping to keep track of the |
65 | changes of sequence and acknowledgment numbers, since the client | |
66 | machine is totally unaware of the modification to the TCP stream. | |
67 | ||
68 | ||
7ba0088d | 69 | References: RFC 959, RFC 2428. |
b7080c8e A |
70 | |
71 | Initial version: August, 1996 (cjm) | |
72 | ||
73 | Version 1.6 | |
74 | Brian Somers and Martin Renters identified an IP checksum | |
75 | error for modified IP packets. | |
76 | ||
77 | Version 1.7: January 9, 1996 (cjm) | |
7ba0088d | 78 | Differential checksum computation for change |
b7080c8e | 79 | in IP packet length. |
7ba0088d | 80 | |
b7080c8e A |
81 | Version 2.1: May, 1997 (cjm) |
82 | Very minor changes to conform with | |
83 | local/global/function naming conventions | |
7ba0088d A |
84 | within the packet aliasing module. |
85 | ||
86 | Version 3.1: May, 2000 (eds) | |
87 | Add support for passive mode, alias the 227 replies. | |
b7080c8e A |
88 | |
89 | See HISTORY file for record of revisions. | |
90 | */ | |
91 | ||
92 | /* Includes */ | |
93 | #include <ctype.h> | |
7ba0088d | 94 | #include <stdio.h> |
b7080c8e A |
95 | #include <string.h> |
96 | #include <sys/types.h> | |
97 | #include <netinet/in_systm.h> | |
98 | #include <netinet/in.h> | |
99 | #include <netinet/ip.h> | |
100 | #include <netinet/tcp.h> | |
101 | ||
102 | #include "alias_local.h" | |
103 | ||
7ba0088d A |
104 | #define FTP_CONTROL_PORT_NUMBER 21 |
105 | #define MAX_MESSAGE_SIZE 128 | |
106 | ||
107 | enum ftp_message_type { | |
108 | FTP_PORT_COMMAND, | |
109 | FTP_EPRT_COMMAND, | |
110 | FTP_227_REPLY, | |
111 | FTP_229_REPLY, | |
112 | FTP_UNKNOWN_MESSAGE | |
113 | }; | |
b7080c8e | 114 | |
7ba0088d A |
115 | static int ParseFtpPortCommand(char *, int); |
116 | static int ParseFtpEprtCommand(char *, int); | |
117 | static int ParseFtp227Reply(char *, int); | |
118 | static int ParseFtp229Reply(char *, int); | |
119 | static void NewFtpMessage(struct ip *, struct alias_link *, int, int); | |
b7080c8e | 120 | |
7ba0088d A |
121 | static struct in_addr true_addr; /* in network byte order. */ |
122 | static u_short true_port; /* in host byte order. */ | |
b7080c8e A |
123 | |
124 | void | |
125 | AliasHandleFtpOut( | |
126 | struct ip *pip, /* IP packet to examine/patch */ | |
127 | struct alias_link *link, /* The link to go through (aliased port) */ | |
128 | int maxpacketsize /* The maximum size this packet can grow to (including headers) */) | |
129 | { | |
130 | int hlen, tlen, dlen; | |
b7080c8e A |
131 | char *sptr; |
132 | struct tcphdr *tc; | |
7ba0088d A |
133 | int ftp_message_type; |
134 | ||
b7080c8e A |
135 | /* Calculate data length of TCP packet */ |
136 | tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); | |
137 | hlen = (pip->ip_hl + tc->th_off) << 2; | |
138 | tlen = ntohs(pip->ip_len); | |
139 | dlen = tlen - hlen; | |
140 | ||
b7080c8e | 141 | /* Place string pointer and beginning of data */ |
7ba0088d | 142 | sptr = (char *) pip; |
b7080c8e A |
143 | sptr += hlen; |
144 | ||
7ba0088d A |
145 | /* |
146 | * Check that data length is not too long and previous message was | |
147 | * properly terminated with CRLF. | |
148 | */ | |
149 | if (dlen <= MAX_MESSAGE_SIZE && GetLastLineCrlfTermed(link)) { | |
150 | ftp_message_type = FTP_UNKNOWN_MESSAGE; | |
151 | ||
152 | if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) { | |
153 | /* | |
154 | * When aliasing a client, check for the PORT/EPRT command. | |
155 | */ | |
156 | if (ParseFtpPortCommand(sptr, dlen)) | |
157 | ftp_message_type = FTP_PORT_COMMAND; | |
158 | else if (ParseFtpEprtCommand(sptr, dlen)) | |
159 | ftp_message_type = FTP_EPRT_COMMAND; | |
160 | } else { | |
161 | /* | |
162 | * When aliasing a server, check for the 227/229 reply. | |
163 | */ | |
164 | if (ParseFtp227Reply(sptr, dlen)) | |
165 | ftp_message_type = FTP_227_REPLY; | |
166 | else if (ParseFtp229Reply(sptr, dlen)) | |
167 | ftp_message_type = FTP_229_REPLY; | |
168 | } | |
169 | ||
170 | if (ftp_message_type != FTP_UNKNOWN_MESSAGE) | |
171 | NewFtpMessage(pip, link, maxpacketsize, ftp_message_type); | |
172 | } | |
173 | ||
174 | /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ | |
175 | ||
176 | if (dlen) { /* only if there's data */ | |
177 | sptr = (char *) pip; /* start over at beginning */ | |
178 | tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may have grown */ | |
179 | SetLastLineCrlfTermed(link, | |
180 | (sptr[tlen-2] == '\r') && (sptr[tlen-1] == '\n')); | |
181 | } | |
182 | } | |
183 | ||
184 | static int | |
185 | ParseFtpPortCommand(char *sptr, int dlen) | |
186 | { | |
187 | char ch; | |
188 | int i, state; | |
189 | u_int32_t addr; | |
190 | u_short port; | |
191 | u_int8_t octet; | |
192 | ||
193 | /* Format: "PORT A,D,D,R,PO,RT". */ | |
194 | ||
195 | /* Return if data length is too short. */ | |
196 | if (dlen < 18) | |
197 | return 0; | |
198 | ||
199 | addr = port = octet = 0; | |
200 | state = -4; | |
201 | for (i = 0; i < dlen; i++) { | |
202 | ch = sptr[i]; | |
203 | switch (state) { | |
204 | case -4: if (ch == 'P') state++; else return 0; break; | |
205 | case -3: if (ch == 'O') state++; else return 0; break; | |
206 | case -2: if (ch == 'R') state++; else return 0; break; | |
207 | case -1: if (ch == 'T') state++; else return 0; break; | |
208 | ||
209 | case 0: | |
210 | if (isspace(ch)) | |
211 | break; | |
212 | else | |
213 | state++; | |
214 | case 1: case 3: case 5: case 7: case 9: case 11: | |
215 | if (isdigit(ch)) { | |
216 | octet = ch - '0'; | |
217 | state++; | |
218 | } else | |
219 | return 0; | |
220 | break; | |
221 | case 2: case 4: case 6: case 8: | |
222 | if (isdigit(ch)) | |
223 | octet = 10 * octet + ch - '0'; | |
224 | else if (ch == ',') { | |
225 | addr = (addr << 8) + octet; | |
226 | state++; | |
227 | } else | |
228 | return 0; | |
229 | break; | |
230 | case 10: case 12: | |
231 | if (isdigit(ch)) | |
232 | octet = 10 * octet + ch - '0'; | |
233 | else if (ch == ',' || state == 12) { | |
234 | port = (port << 8) + octet; | |
235 | state++; | |
236 | } else | |
237 | return 0; | |
238 | break; | |
239 | } | |
240 | } | |
241 | ||
242 | if (state == 13) { | |
243 | true_addr.s_addr = htonl(addr); | |
244 | true_port = port; | |
245 | return 1; | |
246 | } else | |
247 | return 0; | |
248 | } | |
249 | ||
250 | static int | |
251 | ParseFtpEprtCommand(char *sptr, int dlen) | |
252 | { | |
253 | char ch, delim; | |
254 | int i, state; | |
255 | u_int32_t addr; | |
256 | u_short port; | |
257 | u_int8_t octet; | |
258 | ||
259 | /* Format: "EPRT |1|A.D.D.R|PORT|". */ | |
260 | ||
261 | /* Return if data length is too short. */ | |
262 | if (dlen < 18) | |
263 | return 0; | |
264 | ||
265 | addr = port = octet = 0; | |
266 | delim = '|'; /* XXX gcc -Wuninitialized */ | |
267 | state = -4; | |
268 | for (i = 0; i < dlen; i++) { | |
269 | ch = sptr[i]; | |
270 | switch (state) | |
271 | { | |
272 | case -4: if (ch == 'E') state++; else return 0; break; | |
273 | case -3: if (ch == 'P') state++; else return 0; break; | |
274 | case -2: if (ch == 'R') state++; else return 0; break; | |
275 | case -1: if (ch == 'T') state++; else return 0; break; | |
276 | ||
277 | case 0: | |
278 | if (!isspace(ch)) { | |
279 | delim = ch; | |
280 | state++; | |
281 | } | |
282 | break; | |
283 | case 1: | |
284 | if (ch == '1') /* IPv4 address */ | |
285 | state++; | |
286 | else | |
287 | return 0; | |
288 | break; | |
289 | case 2: | |
290 | if (ch == delim) | |
291 | state++; | |
292 | else | |
293 | return 0; | |
294 | break; | |
295 | case 3: case 5: case 7: case 9: | |
296 | if (isdigit(ch)) { | |
297 | octet = ch - '0'; | |
298 | state++; | |
299 | } else | |
300 | return 0; | |
301 | break; | |
302 | case 4: case 6: case 8: case 10: | |
303 | if (isdigit(ch)) | |
304 | octet = 10 * octet + ch - '0'; | |
305 | else if (ch == '.' || state == 10) { | |
306 | addr = (addr << 8) + octet; | |
307 | state++; | |
308 | } else | |
309 | return 0; | |
310 | break; | |
311 | case 11: | |
312 | if (isdigit(ch)) { | |
313 | port = ch - '0'; | |
314 | state++; | |
315 | } else | |
316 | return 0; | |
317 | break; | |
318 | case 12: | |
319 | if (isdigit(ch)) | |
320 | port = 10 * port + ch - '0'; | |
321 | else if (ch == delim) | |
322 | state++; | |
323 | else | |
324 | return 0; | |
325 | break; | |
326 | } | |
327 | } | |
328 | ||
329 | if (state == 13) { | |
330 | true_addr.s_addr = htonl(addr); | |
331 | true_port = port; | |
332 | return 1; | |
333 | } else | |
334 | return 0; | |
335 | } | |
b7080c8e | 336 | |
7ba0088d A |
337 | static int |
338 | ParseFtp227Reply(char *sptr, int dlen) | |
339 | { | |
340 | char ch; | |
341 | int i, state; | |
342 | u_int32_t addr; | |
343 | u_short port; | |
344 | u_int8_t octet; | |
345 | ||
346 | /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */ | |
347 | ||
348 | /* Return if data length is too short. */ | |
349 | if (dlen < 17) | |
350 | return 0; | |
351 | ||
352 | addr = port = octet = 0; | |
353 | ||
354 | state = -3; | |
355 | for (i = 0; i < dlen; i++) { | |
356 | ch = sptr[i]; | |
357 | switch (state) | |
b7080c8e | 358 | { |
7ba0088d A |
359 | case -3: if (ch == '2') state++; else return 0; break; |
360 | case -2: if (ch == '2') state++; else return 0; break; | |
361 | case -1: if (ch == '7') state++; else return 0; break; | |
362 | ||
363 | case 0: | |
364 | if (ch == '(') | |
365 | state++; | |
366 | break; | |
367 | case 1: case 3: case 5: case 7: case 9: case 11: | |
368 | if (isdigit(ch)) { | |
369 | octet = ch - '0'; | |
370 | state++; | |
371 | } else | |
372 | return 0; | |
373 | break; | |
374 | case 2: case 4: case 6: case 8: | |
375 | if (isdigit(ch)) | |
376 | octet = 10 * octet + ch - '0'; | |
377 | else if (ch == ',') { | |
378 | addr = (addr << 8) + octet; | |
379 | state++; | |
380 | } else | |
381 | return 0; | |
382 | break; | |
383 | case 10: case 12: | |
384 | if (isdigit(ch)) | |
385 | octet = 10 * octet + ch - '0'; | |
386 | else if (ch == ',' || (state == 12 && ch == ')')) { | |
387 | port = (port << 8) + octet; | |
388 | state++; | |
389 | } else | |
390 | return 0; | |
391 | break; | |
392 | } | |
b7080c8e | 393 | } |
7ba0088d A |
394 | |
395 | if (state == 13) { | |
396 | true_port = port; | |
397 | true_addr.s_addr = htonl(addr); | |
398 | return 1; | |
399 | } else | |
400 | return 0; | |
401 | } | |
402 | ||
403 | static int | |
404 | ParseFtp229Reply(char *sptr, int dlen) | |
405 | { | |
406 | char ch, delim; | |
407 | int i, state; | |
408 | u_short port; | |
409 | ||
410 | /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */ | |
411 | ||
412 | /* Return if data length is too short. */ | |
413 | if (dlen < 11) | |
414 | return 0; | |
415 | ||
416 | port = 0; | |
417 | delim = '|'; /* XXX gcc -Wuninitialized */ | |
418 | ||
419 | state = -3; | |
420 | for (i = 0; i < dlen; i++) { | |
421 | ch = sptr[i]; | |
422 | switch (state) | |
423 | { | |
424 | case -3: if (ch == '2') state++; else return 0; break; | |
425 | case -2: if (ch == '2') state++; else return 0; break; | |
426 | case -1: if (ch == '9') state++; else return 0; break; | |
427 | ||
428 | case 0: | |
429 | if (ch == '(') | |
430 | state++; | |
431 | break; | |
432 | case 1: | |
433 | delim = ch; | |
434 | state++; | |
435 | break; | |
436 | case 2: case 3: | |
437 | if (ch == delim) | |
438 | state++; | |
439 | else | |
440 | return 0; | |
441 | break; | |
442 | case 4: | |
443 | if (isdigit(ch)) { | |
444 | port = ch - '0'; | |
445 | state++; | |
446 | } else | |
447 | return 0; | |
448 | break; | |
449 | case 5: | |
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 | case 6: | |
458 | if (ch == ')') | |
459 | state++; | |
460 | else | |
461 | return 0; | |
462 | break; | |
463 | } | |
464 | } | |
465 | ||
466 | if (state == 7) { | |
467 | true_port = port; | |
468 | return 1; | |
469 | } else | |
470 | return 0; | |
b7080c8e A |
471 | } |
472 | ||
473 | static void | |
7ba0088d A |
474 | NewFtpMessage(struct ip *pip, |
475 | struct alias_link *link, | |
476 | int maxpacketsize, | |
477 | int ftp_message_type) | |
478 | { | |
b7080c8e A |
479 | struct alias_link *ftp_link; |
480 | ||
7ba0088d A |
481 | /* Security checks. */ |
482 | if (ftp_message_type != FTP_229_REPLY && | |
483 | pip->ip_src.s_addr != true_addr.s_addr) | |
484 | return; | |
485 | ||
486 | if (true_port < IPPORT_RESERVED) | |
487 | return; | |
488 | ||
489 | /* Establish link to address and port found in FTP control message. */ | |
b7080c8e | 490 | ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link), |
7ba0088d | 491 | htons(true_port), 0, IPPROTO_TCP, 1); |
b7080c8e A |
492 | |
493 | if (ftp_link != NULL) | |
494 | { | |
495 | int slen, hlen, tlen, dlen; | |
496 | struct tcphdr *tc; | |
497 | ||
498 | #ifndef NO_FW_PUNCH | |
7ba0088d A |
499 | if (ftp_message_type == FTP_PORT_COMMAND || |
500 | ftp_message_type == FTP_EPRT_COMMAND) { | |
501 | /* Punch hole in firewall */ | |
502 | PunchFWHole(ftp_link); | |
503 | } | |
b7080c8e A |
504 | #endif |
505 | ||
506 | /* Calculate data length of TCP packet */ | |
507 | tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); | |
508 | hlen = (pip->ip_hl + tc->th_off) << 2; | |
509 | tlen = ntohs(pip->ip_len); | |
510 | dlen = tlen - hlen; | |
511 | ||
7ba0088d | 512 | /* Create new FTP message. */ |
b7080c8e | 513 | { |
7ba0088d | 514 | char stemp[MAX_MESSAGE_SIZE + 1]; |
b7080c8e A |
515 | char *sptr; |
516 | u_short alias_port; | |
517 | u_char *ptr; | |
7ba0088d | 518 | int a1, a2, a3, a4, p1, p2; |
b7080c8e | 519 | struct in_addr alias_address; |
7ba0088d | 520 | |
b7080c8e A |
521 | /* Decompose alias address into quad format */ |
522 | alias_address = GetAliasAddress(link); | |
523 | ptr = (u_char *) &alias_address.s_addr; | |
524 | a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr; | |
525 | ||
7ba0088d A |
526 | alias_port = GetAliasPort(ftp_link); |
527 | ||
528 | switch (ftp_message_type) | |
529 | { | |
530 | case FTP_PORT_COMMAND: | |
531 | case FTP_227_REPLY: | |
532 | /* Decompose alias port into pair format. */ | |
533 | ptr = (char *) &alias_port; | |
534 | p1 = *ptr++; p2=*ptr; | |
535 | ||
536 | if (ftp_message_type == FTP_PORT_COMMAND) { | |
537 | /* Generate PORT command string. */ | |
538 | sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", | |
539 | a1,a2,a3,a4,p1,p2); | |
540 | } else { | |
541 | /* Generate 227 reply string. */ | |
542 | sprintf(stemp, | |
543 | "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", | |
544 | a1,a2,a3,a4,p1,p2); | |
545 | } | |
546 | break; | |
547 | case FTP_EPRT_COMMAND: | |
548 | /* Generate EPRT command string. */ | |
549 | sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n", | |
550 | a1,a2,a3,a4,ntohs(alias_port)); | |
551 | break; | |
552 | case FTP_229_REPLY: | |
553 | /* Generate 229 reply string. */ | |
554 | sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n", | |
555 | ntohs(alias_port)); | |
556 | break; | |
557 | } | |
b7080c8e A |
558 | |
559 | /* Save string length for IP header modification */ | |
560 | slen = strlen(stemp); | |
561 | ||
7ba0088d | 562 | /* Copy modified buffer into IP packet. */ |
b7080c8e A |
563 | sptr = (char *) pip; sptr += hlen; |
564 | strncpy(sptr, stemp, maxpacketsize-hlen); | |
565 | } | |
566 | ||
567 | /* Save information regarding modified seq and ack numbers */ | |
568 | { | |
569 | int delta; | |
570 | ||
571 | SetAckModified(link); | |
572 | delta = GetDeltaSeqOut(pip, link); | |
573 | AddSeq(pip, link, delta+slen-dlen); | |
574 | } | |
575 | ||
576 | /* Revise IP header */ | |
577 | { | |
578 | u_short new_len; | |
579 | ||
580 | new_len = htons(hlen + slen); | |
581 | DifferentialChecksum(&pip->ip_sum, | |
582 | &new_len, | |
583 | &pip->ip_len, | |
584 | 1); | |
585 | pip->ip_len = new_len; | |
586 | } | |
587 | ||
588 | /* Compute TCP checksum for revised packet */ | |
589 | tc->th_sum = 0; | |
590 | tc->th_sum = TcpChecksum(pip); | |
591 | } | |
592 | else | |
593 | { | |
594 | #ifdef DEBUG | |
595 | fprintf(stderr, | |
596 | "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); | |
597 | #endif | |
598 | } | |
599 | } |