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->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 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 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 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(":A!a@n.n 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 353 true_port = htons(org_port); 354 true_addr.s_addr = htonl(org_addr); 355 destaddr.s_addr = 0; 356 357 /* Sanity/Security checking */ 358 if (!org_addr || !org_port || 359 pip->ip_src.s_addr != true_addr.s_addr || 360 org_port < IPPORT_RESERVED) 361 goto lBAD_CTCP; 362 363 /* 364 * Steal the FTP_DATA_PORT - it doesn't really 365 * matter, and this would probably allow it through 366 * at least _some_ firewalls. 367 */ 368 dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, 369 true_port, 0, 370 IPPROTO_TCP, 1); 371 DBprintf(("Got a DCC link\n")); 372 if (dcc_lnk) { 373 struct in_addr alias_address; /* Address from aliasing */ 374 u_short alias_port; /* Port given by 375 * aliasing */ 376 int n; 377 378 #ifndef NO_FW_PUNCH 379 /* Generate firewall hole as appropriate */ 380 PunchFWHole(dcc_lnk); 381 #endif 382 383 alias_address = GetAliasAddress(lnk); 384 n = snprintf(&newpacket[iCopy], 385 PKTSIZE - iCopy, 386 "%lu ", (u_long) htonl(alias_address.s_addr)); 387 if (n < 0) { 388 DBprintf(("DCC packet construct failure.\n")); 389 goto lBAD_CTCP; 390 } 391 if ((iCopy += n) >= PKTSIZE) { /* Truncated/fit exactly 392 * - bad news */ 393 DBprintf(("DCC constructed packet overflow.\n")); 394 goto lBAD_CTCP; 395 } 396 alias_port = GetAliasPort(dcc_lnk); 397 n = snprintf(&newpacket[iCopy], 398 PKTSIZE - iCopy, 399 "%u", htons(alias_port)); 400 if (n < 0) { 401 DBprintf(("DCC packet construct failure.\n")); 402 goto lBAD_CTCP; 403 } 404 iCopy += n; 405 /* 406 * Done - truncated cases will be taken 407 * care of by lBAD_CTCP 408 */ 409 DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 410 } 411 } 412 /* 413 * An uninteresting CTCP - state entered right after '\001' 414 * has been pushed. Also used to copy the rest of a DCC, 415 * after IP address and port has been handled 416 */ 417 lBAD_CTCP: 418 for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { 419 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 420 if (sptr[i] == '\001') { 421 goto lNORMAL_TEXT; 422 } 423 } 424 goto lPACKET_DONE; 425 /* Normal text */ 426 lNORMAL_TEXT: 427 for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { 428 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 429 if (sptr[i] == '\001') { 430 goto lCTCP_START; 431 } 432 } 433 /* Handle the end of a packet */ 434 lPACKET_DONE: 435 iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; 436 memcpy(sptr + copyat, newpacket, iCopy); 437 438 /* Save information regarding modified seq and ack numbers */ 439 { 440 int delta; 441 442 SetAckModified(lnk); 443 tc = (struct tcphdr *)ip_next(pip); 444 delta = GetDeltaSeqOut(tc->th_seq, lnk); 445 AddSeq(lnk, delta + copyat + iCopy - dlen, pip->ip_hl, 446 pip->ip_len, tc->th_seq, tc->th_off); 447 } 448 449 /* Revise IP header */ 450 { 451 u_short new_len; 452 453 new_len = htons(hlen + iCopy + copyat); 454 DifferentialChecksum(&pip->ip_sum, 455 &new_len, 456 &pip->ip_len, 457 1); 458 pip->ip_len = new_len; 459 } 460 461 /* Compute TCP checksum for revised packet */ 462 tc->th_sum = 0; 463 #ifdef _KERNEL 464 tc->th_x2 = 1; 465 #else 466 tc->th_sum = TcpChecksum(pip); 467 #endif 468 return; 469 } 470 } 471 472 /* Notes: 473 [Note 1] 474 The initial search will most often fail; it could be replaced with a 32-bit specific search. 475 Such a search would be done for 32-bit unsigned value V: 476 V ^= 0x01010101; (Search is for null bytes) 477 if( ((V-0x01010101)^V) & 0x80808080 ) { 478 (found a null bytes which was a 01 byte) 479 } 480 To assert that the processor is 32-bits, do 481 extern int ircdccar[32]; (32 bits) 482 extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 483 which will generate a type-error on all but 32-bit machines. 484 485 [Note 2] This routine really ought to be replaced with one that 486 creates a transparent proxy on the aliasing host, to allow arbitrary 487 changes in the TCP stream. This should not be too difficult given 488 this base; I (ee) will try to do this some time later. 489 */ 490