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