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 | #include <sys/cdefs.h>
|
---|
28 | __FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_irc.c,v 1.23.2.2.4.1 2009/04/15 03:14:26 kensmith Exp $");
|
---|
29 |
|
---|
30 | /* Alias_irc.c intercepts packages contain IRC CTCP commands, and
|
---|
31 | changes DCC commands to export a port on the aliasing host instead
|
---|
32 | of an aliased host.
|
---|
33 |
|
---|
34 | For this routine to work, the DCC command must fit entirely into a
|
---|
35 | single TCP packet. This will usually happen, but is not
|
---|
36 | guaranteed.
|
---|
37 |
|
---|
38 | The interception is likely to change the length of the packet.
|
---|
39 | The handling of this is copied more-or-less verbatim from
|
---|
40 | ftp_alias.c
|
---|
41 |
|
---|
42 | Initial version: Eivind Eklund <[email protected]> (ee) 97-01-29
|
---|
43 |
|
---|
44 | Version 2.1: May, 1997 (cjm)
|
---|
45 | Very minor changes to conform with
|
---|
46 | local/global/function naming conventions
|
---|
47 | withing the packet alising module.
|
---|
48 | */
|
---|
49 |
|
---|
50 | /* Includes */
|
---|
51 | #ifdef _KERNEL
|
---|
52 | #include <sys/param.h>
|
---|
53 | #include <sys/ctype.h>
|
---|
54 | #include <sys/limits.h>
|
---|
55 | #include <sys/systm.h>
|
---|
56 | #include <sys/kernel.h>
|
---|
57 | #include <sys/module.h>
|
---|
58 | #else
|
---|
59 | #include <errno.h>
|
---|
60 | #include <sys/types.h>
|
---|
61 | #include <stdio.h>
|
---|
62 | #include <stdlib.h>
|
---|
63 | #include <string.h>
|
---|
64 | #include <limits.h>
|
---|
65 | #endif
|
---|
66 |
|
---|
67 | #include <netinet/in_systm.h>
|
---|
68 | #include <netinet/in.h>
|
---|
69 | #include <netinet/ip.h>
|
---|
70 | #include <netinet/tcp.h>
|
---|
71 |
|
---|
72 | #ifdef _KERNEL
|
---|
73 | #include <netinet/libalias/alias.h>
|
---|
74 | #include <netinet/libalias/alias_local.h>
|
---|
75 | #include <netinet/libalias/alias_mod.h>
|
---|
76 | #else
|
---|
77 | #include "alias_local.h"
|
---|
78 | #include "alias_mod.h"
|
---|
79 | #endif
|
---|
80 |
|
---|
81 | #define IRC_CONTROL_PORT_NUMBER_1 6667
|
---|
82 | #define IRC_CONTROL_PORT_NUMBER_2 6668
|
---|
83 |
|
---|
84 | #define PKTSIZE (IP_MAXPACKET + 1)
|
---|
85 | char *newpacket;
|
---|
86 |
|
---|
87 | /* Local defines */
|
---|
88 | #define DBprintf(a)
|
---|
89 |
|
---|
90 | static void
|
---|
91 | AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *,
|
---|
92 | int maxpacketsize);
|
---|
93 |
|
---|
94 | static int
|
---|
95 | fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
|
---|
96 | {
|
---|
97 |
|
---|
98 | if (ah->dport == NULL || ah->lnk == NULL ||
|
---|
99 | ah->maxpktsize == 0)
|
---|
100 | return (-1);
|
---|
101 | if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1
|
---|
102 | || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2)
|
---|
103 | return (0);
|
---|
104 | return (-1);
|
---|
105 | }
|
---|
106 |
|
---|
107 | static int
|
---|
108 | protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
|
---|
109 | {
|
---|
110 |
|
---|
111 | newpacket = malloc(PKTSIZE);
|
---|
112 | if (newpacket) {
|
---|
113 | AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize);
|
---|
114 | free(newpacket);
|
---|
115 | }
|
---|
116 | return (0);
|
---|
117 | }
|
---|
118 |
|
---|
119 | struct proto_handler handlers[] = {
|
---|
120 | {
|
---|
121 | .pri = 90,
|
---|
122 | .dir = OUT,
|
---|
123 | .proto = TCP,
|
---|
124 | .fingerprint = &fingerprint,
|
---|
125 | .protohandler = &protohandler
|
---|
126 | },
|
---|
127 | { EOH }
|
---|
128 | };
|
---|
129 |
|
---|
130 | static int
|
---|
131 | mod_handler(module_t mod, int type, void *data)
|
---|
132 | {
|
---|
133 | int error;
|
---|
134 |
|
---|
135 | switch (type) {
|
---|
136 | case MOD_LOAD:
|
---|
137 | error = 0;
|
---|
138 | LibAliasAttachHandlers(handlers);
|
---|
139 | break;
|
---|
140 | case MOD_UNLOAD:
|
---|
141 | error = 0;
|
---|
142 | LibAliasDetachHandlers(handlers);
|
---|
143 | break;
|
---|
144 | default:
|
---|
145 | error = EINVAL;
|
---|
146 | }
|
---|
147 | return (error);
|
---|
148 | }
|
---|
149 |
|
---|
150 | #ifdef _KERNEL
|
---|
151 | static
|
---|
152 | #endif
|
---|
153 | moduledata_t alias_mod = {
|
---|
154 | "alias_irc", mod_handler, NULL
|
---|
155 | };
|
---|
156 |
|
---|
157 | /* Kernel module definition. */
|
---|
158 | #ifdef _KERNEL
|
---|
159 | DECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
|
---|
160 | MODULE_VERSION(alias_irc, 1);
|
---|
161 | MODULE_DEPEND(alias_irc, libalias, 1, 1, 1);
|
---|
162 | #endif
|
---|
163 |
|
---|
164 | static void
|
---|
165 | AliasHandleIrcOut(struct libalias *la,
|
---|
166 | struct ip *pip, /* IP packet to examine */
|
---|
167 | struct alias_link *lnk, /* Which link are we on? */
|
---|
168 | int maxsize /* Maximum size of IP packet including
|
---|
169 | * headers */
|
---|
170 | )
|
---|
171 | {
|
---|
172 | int hlen, tlen, dlen;
|
---|
173 | struct in_addr true_addr;
|
---|
174 | u_short true_port;
|
---|
175 | char *sptr;
|
---|
176 | struct tcphdr *tc;
|
---|
177 | int i; /* Iterator through the source */
|
---|
178 |
|
---|
179 | /* Calculate data length of TCP packet */
|
---|
180 | tc = (struct tcphdr *)ip_next(pip);
|
---|
181 | hlen = (pip->ip_hl + tc->th_off) << 2;
|
---|
182 | tlen = ntohs(pip->ip_len);
|
---|
183 | dlen = tlen - hlen;
|
---|
184 |
|
---|
185 | /*
|
---|
186 | * Return if data length is too short - assume an entire PRIVMSG in
|
---|
187 | * each packet.
|
---|
188 | */
|
---|
189 | if (dlen < (int)sizeof(":[email protected] PRIVMSG A :aDCC 1 1a") - 1)
|
---|
190 | return;
|
---|
191 |
|
---|
192 | /* Place string pointer at beginning of data */
|
---|
193 | sptr = (char *)pip;
|
---|
194 | sptr += hlen;
|
---|
195 | maxsize -= hlen; /* We're interested in maximum size of
|
---|
196 | * data, not packet */
|
---|
197 |
|
---|
198 | /* Search for a CTCP command [Note 1] */
|
---|
199 | for (i = 0; i < dlen; i++) {
|
---|
200 | if (sptr[i] == '\001')
|
---|
201 | goto lFOUND_CTCP;
|
---|
202 | }
|
---|
203 | return; /* No CTCP commands in */
|
---|
204 | /* Handle CTCP commands - the buffer may have to be copied */
|
---|
205 | lFOUND_CTCP:
|
---|
206 | {
|
---|
207 | unsigned int copyat = i;
|
---|
208 | unsigned int iCopy = 0; /* How much data have we written to
|
---|
209 | * copy-back string? */
|
---|
210 | unsigned long org_addr; /* Original IP address */
|
---|
211 | unsigned short org_port; /* Original source port
|
---|
212 | * address */
|
---|
213 |
|
---|
214 | lCTCP_START:
|
---|
215 | if (i >= dlen || iCopy >= PKTSIZE)
|
---|
216 | goto lPACKET_DONE;
|
---|
217 | newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start
|
---|
218 | * character */
|
---|
219 | /* Start of a CTCP */
|
---|
220 | if (i + 4 >= dlen) /* Too short for DCC */
|
---|
221 | goto lBAD_CTCP;
|
---|
222 | if (sptr[i + 0] != 'D')
|
---|
223 | goto lBAD_CTCP;
|
---|
224 | if (sptr[i + 1] != 'C')
|
---|
225 | goto lBAD_CTCP;
|
---|
226 | if (sptr[i + 2] != 'C')
|
---|
227 | goto lBAD_CTCP;
|
---|
228 | if (sptr[i + 3] != ' ')
|
---|
229 | goto lBAD_CTCP;
|
---|
230 | /* We have a DCC command - handle it! */
|
---|
231 | i += 4; /* Skip "DCC " */
|
---|
232 | if (iCopy + 4 > PKTSIZE)
|
---|
233 | goto lPACKET_DONE;
|
---|
234 | newpacket[iCopy++] = 'D';
|
---|
235 | newpacket[iCopy++] = 'C';
|
---|
236 | newpacket[iCopy++] = 'C';
|
---|
237 | newpacket[iCopy++] = ' ';
|
---|
238 |
|
---|
239 | DBprintf(("Found DCC\n"));
|
---|
240 | /*
|
---|
241 | * Skip any extra spaces (should not occur according to
|
---|
242 | * protocol, but DCC breaks CTCP protocol anyway
|
---|
243 | */
|
---|
244 | while (sptr[i] == ' ') {
|
---|
245 | if (++i >= dlen) {
|
---|
246 | DBprintf(("DCC packet terminated in just spaces\n"));
|
---|
247 | goto lPACKET_DONE;
|
---|
248 | }
|
---|
249 | }
|
---|
250 |
|
---|
251 | DBprintf(("Transferring command...\n"));
|
---|
252 | while (sptr[i] != ' ') {
|
---|
253 | newpacket[iCopy++] = sptr[i];
|
---|
254 | if (++i >= dlen || iCopy >= PKTSIZE) {
|
---|
255 | DBprintf(("DCC packet terminated during command\n"));
|
---|
256 | goto lPACKET_DONE;
|
---|
257 | }
|
---|
258 | }
|
---|
259 | /* Copy _one_ space */
|
---|
260 | if (i + 1 < dlen && iCopy < PKTSIZE)
|
---|
261 | newpacket[iCopy++] = sptr[i++];
|
---|
262 |
|
---|
263 | DBprintf(("Done command - removing spaces\n"));
|
---|
264 | /*
|
---|
265 | * Skip any extra spaces (should not occur according to
|
---|
266 | * protocol, but DCC breaks CTCP protocol anyway
|
---|
267 | */
|
---|
268 | while (sptr[i] == ' ') {
|
---|
269 | if (++i >= dlen) {
|
---|
270 | DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
|
---|
271 | goto lPACKET_DONE;
|
---|
272 | }
|
---|
273 | }
|
---|
274 |
|
---|
275 | DBprintf(("Transferring filename...\n"));
|
---|
276 | while (sptr[i] != ' ') {
|
---|
277 | newpacket[iCopy++] = sptr[i];
|
---|
278 | if (++i >= dlen || iCopy >= PKTSIZE) {
|
---|
279 | DBprintf(("DCC packet terminated during filename\n"));
|
---|
280 | goto lPACKET_DONE;
|
---|
281 | }
|
---|
282 | }
|
---|
283 | /* Copy _one_ space */
|
---|
284 | if (i + 1 < dlen && iCopy < PKTSIZE)
|
---|
285 | newpacket[iCopy++] = sptr[i++];
|
---|
286 |
|
---|
287 | DBprintf(("Done filename - removing spaces\n"));
|
---|
288 | /*
|
---|
289 | * Skip any extra spaces (should not occur according to
|
---|
290 | * protocol, but DCC breaks CTCP protocol anyway
|
---|
291 | */
|
---|
292 | while (sptr[i] == ' ') {
|
---|
293 | if (++i >= dlen) {
|
---|
294 | DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
|
---|
295 | goto lPACKET_DONE;
|
---|
296 | }
|
---|
297 | }
|
---|
298 |
|
---|
299 | DBprintf(("Fetching IP address\n"));
|
---|
300 | /* Fetch IP address */
|
---|
301 | org_addr = 0;
|
---|
302 | while (i < dlen && isdigit(sptr[i])) {
|
---|
303 | if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */
|
---|
304 | DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
|
---|
305 | goto lBAD_CTCP;
|
---|
306 | }
|
---|
307 | org_addr *= 10;
|
---|
308 | org_addr += sptr[i++] - '0';
|
---|
309 | }
|
---|
310 | DBprintf(("Skipping space\n"));
|
---|
311 | if (i + 1 >= dlen || sptr[i] != ' ') {
|
---|
312 | DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i]));
|
---|
313 | goto lBAD_CTCP;
|
---|
314 | }
|
---|
315 | /*
|
---|
316 | * Skip any extra spaces (should not occur according to
|
---|
317 | * protocol, but DCC breaks CTCP protocol anyway, so we
|
---|
318 | * might as well play it safe
|
---|
319 | */
|
---|
320 | while (sptr[i] == ' ') {
|
---|
321 | if (++i >= dlen) {
|
---|
322 | DBprintf(("Packet failure - space overflow.\n"));
|
---|
323 | goto lPACKET_DONE;
|
---|
324 | }
|
---|
325 | }
|
---|
326 | DBprintf(("Fetching port number\n"));
|
---|
327 | /* Fetch source port */
|
---|
328 | org_port = 0;
|
---|
329 | while (i < dlen && isdigit(sptr[i])) {
|
---|
330 | if (org_port > 6554) { /* Terminate on overflow
|
---|
331 | * (65536/10 rounded up */
|
---|
332 | DBprintf(("DCC: port number overflow\n"));
|
---|
333 | goto lBAD_CTCP;
|
---|
334 | }
|
---|
335 | org_port *= 10;
|
---|
336 | org_port += sptr[i++] - '0';
|
---|
337 | }
|
---|
338 | /* Skip illegal addresses (or early termination) */
|
---|
339 | if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) {
|
---|
340 | DBprintf(("Bad port termination\n"));
|
---|
341 | goto lBAD_CTCP;
|
---|
342 | }
|
---|
343 | DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
|
---|
344 |
|
---|
345 | /* We've got the address and port - now alias it */
|
---|
346 | {
|
---|
347 | struct alias_link *dcc_lnk;
|
---|
348 | struct in_addr destaddr;
|
---|
349 |
|
---|
350 |
|
---|
351 | true_port = htons(org_port);
|
---|
352 | true_addr.s_addr = htonl(org_addr);
|
---|
353 | destaddr.s_addr = 0;
|
---|
354 |
|
---|
355 | /* Sanity/Security checking */
|
---|
356 | if (!org_addr || !org_port ||
|
---|
357 | pip->ip_src.s_addr != true_addr.s_addr ||
|
---|
358 | org_port < IPPORT_RESERVED)
|
---|
359 | goto lBAD_CTCP;
|
---|
360 |
|
---|
361 | /*
|
---|
362 | * Steal the FTP_DATA_PORT - it doesn't really
|
---|
363 | * matter, and this would probably allow it through
|
---|
364 | * at least _some_ firewalls.
|
---|
365 | */
|
---|
366 | dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr,
|
---|
367 | true_port, 0,
|
---|
368 | IPPROTO_TCP, 1);
|
---|
369 | DBprintf(("Got a DCC link\n"));
|
---|
370 | if (dcc_lnk) {
|
---|
371 | struct in_addr alias_address; /* Address from aliasing */
|
---|
372 | u_short alias_port; /* Port given by
|
---|
373 | * aliasing */
|
---|
374 | int n;
|
---|
375 |
|
---|
376 | #ifndef NO_FW_PUNCH
|
---|
377 | /* Generate firewall hole as appropriate */
|
---|
378 | PunchFWHole(dcc_lnk);
|
---|
379 | #endif
|
---|
380 |
|
---|
381 | alias_address = GetAliasAddress(lnk);
|
---|
382 | n = snprintf(&newpacket[iCopy],
|
---|
383 | PKTSIZE - iCopy,
|
---|
384 | "%lu ", (u_long) htonl(alias_address.s_addr));
|
---|
385 | if (n < 0) {
|
---|
386 | DBprintf(("DCC packet construct failure.\n"));
|
---|
387 | goto lBAD_CTCP;
|
---|
388 | }
|
---|
389 | if ((iCopy += n) >= PKTSIZE) { /* Truncated/fit exactly
|
---|
390 | * - bad news */
|
---|
391 | DBprintf(("DCC constructed packet overflow.\n"));
|
---|
392 | goto lBAD_CTCP;
|
---|
393 | }
|
---|
394 | alias_port = GetAliasPort(dcc_lnk);
|
---|
395 | n = snprintf(&newpacket[iCopy],
|
---|
396 | PKTSIZE - iCopy,
|
---|
397 | "%u", htons(alias_port));
|
---|
398 | if (n < 0) {
|
---|
399 | DBprintf(("DCC packet construct failure.\n"));
|
---|
400 | goto lBAD_CTCP;
|
---|
401 | }
|
---|
402 | iCopy += n;
|
---|
403 | /*
|
---|
404 | * Done - truncated cases will be taken
|
---|
405 | * care of by lBAD_CTCP
|
---|
406 | */
|
---|
407 | DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
|
---|
408 | }
|
---|
409 | }
|
---|
410 | /*
|
---|
411 | * An uninteresting CTCP - state entered right after '\001'
|
---|
412 | * has been pushed. Also used to copy the rest of a DCC,
|
---|
413 | * after IP address and port has been handled
|
---|
414 | */
|
---|
415 | lBAD_CTCP:
|
---|
416 | for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
|
---|
417 | newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
|
---|
418 | if (sptr[i] == '\001') {
|
---|
419 | goto lNORMAL_TEXT;
|
---|
420 | }
|
---|
421 | }
|
---|
422 | goto lPACKET_DONE;
|
---|
423 | /* Normal text */
|
---|
424 | lNORMAL_TEXT:
|
---|
425 | for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
|
---|
426 | newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
|
---|
427 | if (sptr[i] == '\001') {
|
---|
428 | goto lCTCP_START;
|
---|
429 | }
|
---|
430 | }
|
---|
431 | /* Handle the end of a packet */
|
---|
432 | lPACKET_DONE:
|
---|
433 | iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy;
|
---|
434 | memcpy(sptr + copyat, newpacket, iCopy);
|
---|
435 |
|
---|
436 | /* Save information regarding modified seq and ack numbers */
|
---|
437 | {
|
---|
438 | int delta;
|
---|
439 |
|
---|
440 | SetAckModified(lnk);
|
---|
441 | delta = GetDeltaSeqOut(pip, lnk);
|
---|
442 | AddSeq(pip, lnk, delta + copyat + iCopy - dlen);
|
---|
443 | }
|
---|
444 |
|
---|
445 | /* Revise IP header */
|
---|
446 | {
|
---|
447 | u_short new_len;
|
---|
448 |
|
---|
449 | new_len = htons(hlen + iCopy + copyat);
|
---|
450 | DifferentialChecksum(&pip->ip_sum,
|
---|
451 | &new_len,
|
---|
452 | &pip->ip_len,
|
---|
453 | 1);
|
---|
454 | pip->ip_len = new_len;
|
---|
455 | }
|
---|
456 |
|
---|
457 | /* Compute TCP checksum for revised packet */
|
---|
458 | tc->th_sum = 0;
|
---|
459 | #ifdef _KERNEL
|
---|
460 | tc->th_x2 = 1;
|
---|
461 | #else
|
---|
462 | tc->th_sum = TcpChecksum(pip);
|
---|
463 | #endif
|
---|
464 | return;
|
---|
465 | }
|
---|
466 | }
|
---|
467 |
|
---|
468 | /* Notes:
|
---|
469 | [Note 1]
|
---|
470 | The initial search will most often fail; it could be replaced with a 32-bit specific search.
|
---|
471 | Such a search would be done for 32-bit unsigned value V:
|
---|
472 | V ^= 0x01010101; (Search is for null bytes)
|
---|
473 | if( ((V-0x01010101)^V) & 0x80808080 ) {
|
---|
474 | (found a null bytes which was a 01 byte)
|
---|
475 | }
|
---|
476 | To assert that the processor is 32-bits, do
|
---|
477 | extern int ircdccar[32]; (32 bits)
|
---|
478 | extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
|
---|
479 | which will generate a type-error on all but 32-bit machines.
|
---|
480 |
|
---|
481 | [Note 2] This routine really ought to be replaced with one that
|
---|
482 | creates a transparent proxy on the aliasing host, to allow arbitary
|
---|
483 | changes in the TCP stream. This should not be too difficult given
|
---|
484 | this base; I (ee) will try to do this some time later.
|
---|
485 | */
|
---|