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 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