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