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