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